1 // 4.0.25 (2014-04-30)
  2 
  3 /**
  4  * Compiled inline version. (Library mode)
  5  */
  6 
  7 /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
  8 /*globals $code */
  9 
 10 (function(exports, undefined) {
 11 	"use strict";
 12 
 13 	var modules = {};
 14 
 15 	function require(ids, callback) {
 16 		var module, defs = [];
 17 
 18 		for (var i = 0; i < ids.length; ++i) {
 19 			module = modules[ids[i]] || resolve(ids[i]);
 20 			if (!module) {
 21 				throw 'module definition dependecy not found: ' + ids[i];
 22 			}
 23 
 24 			defs.push(module);
 25 		}
 26 
 27 		callback.apply(null, defs);
 28 	}
 29 
 30 	function define(id, dependencies, definition) {
 31 		if (typeof id !== 'string') {
 32 			throw 'invalid module definition, module id must be defined and be a string';
 33 		}
 34 
 35 		if (dependencies === undefined) {
 36 			throw 'invalid module definition, dependencies must be specified';
 37 		}
 38 
 39 		if (definition === undefined) {
 40 			throw 'invalid module definition, definition function must be specified';
 41 		}
 42 
 43 		require(dependencies, function() {
 44 			modules[id] = definition.apply(null, arguments);
 45 		});
 46 	}
 47 
 48 	function defined(id) {
 49 		return !!modules[id];
 50 	}
 51 
 52 	function resolve(id) {
 53 		var target = exports;
 54 		var fragments = id.split(/[.\/]/);
 55 
 56 		for (var fi = 0; fi < fragments.length; ++fi) {
 57 			if (!target[fragments[fi]]) {
 58 				return;
 59 			}
 60 
 61 			target = target[fragments[fi]];
 62 		}
 63 
 64 		return target;
 65 	}
 66 
 67 	function expose(ids) {
 68 		for (var i = 0; i < ids.length; i++) {
 69 			var target = exports;
 70 			var id = ids[i];
 71 			var fragments = id.split(/[.\/]/);
 72 
 73 			for (var fi = 0; fi < fragments.length - 1; ++fi) {
 74 				if (target[fragments[fi]] === undefined) {
 75 					target[fragments[fi]] = {};
 76 				}
 77 
 78 				target = target[fragments[fi]];
 79 			}
 80 
 81 			target[fragments[fragments.length - 1]] = modules[id];
 82 		}
 83 	}
 84 
 85 // Included from: js/tinymce/classes/dom/EventUtils.js
 86 
 87 /**
 88  * EventUtils.js
 89  *
 90  * Copyright, Moxiecode Systems AB
 91  * Released under LGPL License.
 92  *
 93  * License: http://www.tinymce.com/license
 94  * Contributing: http://www.tinymce.com/contributing
 95  */
 96 
 97 /*jshint loopfunc:true*/
 98 /*eslint no-loop-func:0 */
 99 
100 define("tinymce/dom/EventUtils", [], function() {
101 	"use strict";
102 
103 	var eventExpandoPrefix = "mce-data-";
104 	var mouseEventRe = /^(?:mouse|contextmenu)|click/;
105 	var deprecated = {keyLocation: 1, layerX: 1, layerY: 1, returnValue: 1};
106 
107 	/**
108 	 * Binds a native event to a callback on the speified target.
109 	 */
110 	function addEvent(target, name, callback, capture) {
111 		if (target.addEventListener) {
112 			target.addEventListener(name, callback, capture || false);
113 		} else if (target.attachEvent) {
114 			target.attachEvent('on' + name, callback);
115 		}
116 	}
117 
118 	/**
119 	 * Unbinds a native event callback on the specified target.
120 	 */
121 	function removeEvent(target, name, callback, capture) {
122 		if (target.removeEventListener) {
123 			target.removeEventListener(name, callback, capture || false);
124 		} else if (target.detachEvent) {
125 			target.detachEvent('on' + name, callback);
126 		}
127 	}
128 
129 	/**
130 	 * Normalizes a native event object or just adds the event specific methods on a custom event.
131 	 */
132 	function fix(originalEvent, data) {
133 		var name, event = data || {}, undef;
134 
135 		// Dummy function that gets replaced on the delegation state functions
136 		function returnFalse() {
137 			return false;
138 		}
139 
140 		// Dummy function that gets replaced on the delegation state functions
141 		function returnTrue() {
142 			return true;
143 		}
144 
145 		// Copy all properties from the original event
146 		for (name in originalEvent) {
147 			// layerX/layerY is deprecated in Chrome and produces a warning
148 			if (!deprecated[name]) {
149 				event[name] = originalEvent[name];
150 			}
151 		}
152 
153 		// Normalize target IE uses srcElement
154 		if (!event.target) {
155 			event.target = event.srcElement || document;
156 		}
157 
158 		// Calculate pageX/Y if missing and clientX/Y available
159 		if (originalEvent && mouseEventRe.test(originalEvent.type) && originalEvent.pageX === undef && originalEvent.clientX !== undef) {
160 			var eventDoc = event.target.ownerDocument || document;
161 			var doc = eventDoc.documentElement;
162 			var body = eventDoc.body;
163 
164 			event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
165 				( doc && doc.clientLeft || body && body.clientLeft || 0);
166 
167 			event.pageY = originalEvent.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0 ) -
168 				( doc && doc.clientTop  || body && body.clientTop  || 0);
169 		}
170 
171 		// Add preventDefault method
172 		event.preventDefault = function() {
173 			event.isDefaultPrevented = returnTrue;
174 
175 			// Execute preventDefault on the original event object
176 			if (originalEvent) {
177 				if (originalEvent.preventDefault) {
178 					originalEvent.preventDefault();
179 				} else {
180 					originalEvent.returnValue = false; // IE
181 				}
182 			}
183 		};
184 
185 		// Add stopPropagation
186 		event.stopPropagation = function() {
187 			event.isPropagationStopped = returnTrue;
188 
189 			// Execute stopPropagation on the original event object
190 			if (originalEvent) {
191 				if (originalEvent.stopPropagation) {
192 					originalEvent.stopPropagation();
193 				} else {
194 					originalEvent.cancelBubble = true; // IE
195 				}
196 			}
197 		};
198 
199 		// Add stopImmediatePropagation
200 		event.stopImmediatePropagation = function() {
201 			event.isImmediatePropagationStopped = returnTrue;
202 			event.stopPropagation();
203 		};
204 
205 		// Add event delegation states
206 		if (!event.isDefaultPrevented) {
207 			event.isDefaultPrevented = returnFalse;
208 			event.isPropagationStopped = returnFalse;
209 			event.isImmediatePropagationStopped = returnFalse;
210 		}
211 
212 		return event;
213 	}
214 
215 	/**
216 	 * Bind a DOMContentLoaded event across browsers and executes the callback once the page DOM is initialized.
217 	 * It will also set/check the domLoaded state of the event_utils instance so ready isn't called multiple times.
218 	 */
219 	function bindOnReady(win, callback, eventUtils) {
220 		var doc = win.document, event = {type: 'ready'};
221 
222 		if (eventUtils.domLoaded) {
223 			callback(event);
224 			return;
225 		}
226 
227 		// Gets called when the DOM is ready
228 		function readyHandler() {
229 			if (!eventUtils.domLoaded) {
230 				eventUtils.domLoaded = true;
231 				callback(event);
232 			}
233 		}
234 
235 		function waitForDomLoaded() {
236 			// Check complete or interactive state if there is a body
237 			// element on some iframes IE 8 will produce a null body
238 			if (doc.readyState === "complete" || (doc.readyState === "interactive" && doc.body)) {
239 				removeEvent(doc, "readystatechange", waitForDomLoaded);
240 				readyHandler();
241 			}
242 		}
243 
244 		function tryScroll() {
245 			try {
246 				// If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.
247 				// http://javascript.nwbox.com/IEContentLoaded/
248 				doc.documentElement.doScroll("left");
249 			} catch (ex) {
250 				setTimeout(tryScroll, 0);
251 				return;
252 			}
253 
254 			readyHandler();
255 		}
256 
257 		// Use W3C method
258 		if (doc.addEventListener) {
259 			if (doc.readyState === "complete") {
260 				readyHandler();
261 			} else {
262 				addEvent(win, 'DOMContentLoaded', readyHandler);
263 			}
264 		} else {
265 			// Use IE method
266 			addEvent(doc, "readystatechange", waitForDomLoaded);
267 
268 			// Wait until we can scroll, when we can the DOM is initialized
269 			if (doc.documentElement.doScroll && win.self === win.top) {
270 				tryScroll();
271 			}
272 		}
273 
274 		// Fallback if any of the above methods should fail for some odd reason
275 		addEvent(win, 'load', readyHandler);
276 	}
277 
278 	/**
279 	 * This class enables you to bind/unbind native events to elements and normalize it's behavior across browsers.
280 	 */
281 	function EventUtils() {
282 		var self = this, events = {}, count, expando, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;
283 
284 		expando = eventExpandoPrefix + (+new Date()).toString(32);
285 		hasMouseEnterLeave = "onmouseenter" in document.documentElement;
286 		hasFocusIn = "onfocusin" in document.documentElement;
287 		mouseEnterLeave = {mouseenter: 'mouseover', mouseleave: 'mouseout'};
288 		count = 1;
289 
290 		// State if the DOMContentLoaded was executed or not
291 		self.domLoaded = false;
292 		self.events = events;
293 
294 		/**
295 		 * Executes all event handler callbacks for a specific event.
296 		 *
297 		 * @private
298 		 * @param {Event} evt Event object.
299 		 * @param {String} id Expando id value to look for.
300 		 */
301 		function executeHandlers(evt, id) {
302 			var callbackList, i, l, callback, container = events[id];
303 
304 			callbackList = container && container[evt.type];
305 			if (callbackList) {
306 				for (i = 0, l = callbackList.length; i < l; i++) {
307 					callback = callbackList[i];
308 
309 					// Check if callback exists might be removed if a unbind is called inside the callback
310 					if (callback && callback.func.call(callback.scope, evt) === false) {
311 						evt.preventDefault();
312 					}
313 
314 					// Should we stop propagation to immediate listeners
315 					if (evt.isImmediatePropagationStopped()) {
316 						return;
317 					}
318 				}
319 			}
320 		}
321 
322 		/**
323 		 * Binds a callback to an event on the specified target.
324 		 *
325 		 * @method bind
326 		 * @param {Object} target Target node/window or custom object.
327 		 * @param {String} names Name of the event to bind.
328 		 * @param {function} callback Callback function to execute when the event occurs.
329 		 * @param {Object} scope Scope to call the callback function on, defaults to target.
330 		 * @return {function} Callback function that got bound.
331 		 */
332 		self.bind = function(target, names, callback, scope) {
333 			var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window;
334 
335 			// Native event handler function patches the event and executes the callbacks for the expando
336 			function defaultNativeHandler(evt) {
337 				executeHandlers(fix(evt || win.event), id);
338 			}
339 
340 			// Don't bind to text nodes or comments
341 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
342 				return;
343 			}
344 
345 			// Create or get events id for the target
346 			if (!target[expando]) {
347 				id = count++;
348 				target[expando] = id;
349 				events[id] = {};
350 			} else {
351 				id = target[expando];
352 			}
353 
354 			// Setup the specified scope or use the target as a default
355 			scope = scope || target;
356 
357 			// Split names and bind each event, enables you to bind multiple events with one call
358 			names = names.split(' ');
359 			i = names.length;
360 			while (i--) {
361 				name = names[i];
362 				nativeHandler = defaultNativeHandler;
363 				fakeName = capture = false;
364 
365 				// Use ready instead of DOMContentLoaded
366 				if (name === "DOMContentLoaded") {
367 					name = "ready";
368 				}
369 
370 				// DOM is already ready
371 				if (self.domLoaded && name === "ready" && target.readyState == 'complete') {
372 					callback.call(scope, fix({type: name}));
373 					continue;
374 				}
375 
376 				// Handle mouseenter/mouseleaver
377 				if (!hasMouseEnterLeave) {
378 					fakeName = mouseEnterLeave[name];
379 
380 					if (fakeName) {
381 						nativeHandler = function(evt) {
382 							var current, related;
383 
384 							current = evt.currentTarget;
385 							related = evt.relatedTarget;
386 
387 							// Check if related is inside the current target if it's not then the event should
388 							// be ignored since it's a mouseover/mouseout inside the element
389 							if (related && current.contains) {
390 								// Use contains for performance
391 								related = current.contains(related);
392 							} else {
393 								while (related && related !== current) {
394 									related = related.parentNode;
395 								}
396 							}
397 
398 							// Fire fake event
399 							if (!related) {
400 								evt = fix(evt || win.event);
401 								evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter';
402 								evt.target = current;
403 								executeHandlers(evt, id);
404 							}
405 						};
406 					}
407 				}
408 
409 				// Fake bubbeling of focusin/focusout
410 				if (!hasFocusIn && (name === "focusin" || name === "focusout")) {
411 					capture = true;
412 					fakeName = name === "focusin" ? "focus" : "blur";
413 					nativeHandler = function(evt) {
414 						evt = fix(evt || win.event);
415 						evt.type = evt.type === 'focus' ? 'focusin' : 'focusout';
416 						executeHandlers(evt, id);
417 					};
418 				}
419 
420 				// Setup callback list and bind native event
421 				callbackList = events[id][name];
422 				if (!callbackList) {
423 					events[id][name] = callbackList = [{func: callback, scope: scope}];
424 					callbackList.fakeName = fakeName;
425 					callbackList.capture = capture;
426 
427 					// Add the nativeHandler to the callback list so that we can later unbind it
428 					callbackList.nativeHandler = nativeHandler;
429 
430 					// Check if the target has native events support
431 
432 					if (name === "ready") {
433 						bindOnReady(target, nativeHandler, self);
434 					} else {
435 						addEvent(target, fakeName || name, nativeHandler, capture);
436 					}
437 				} else {
438 					if (name === "ready" && self.domLoaded) {
439 						callback({type: name});
440 					} else {
441 						// If it already has an native handler then just push the callback
442 						callbackList.push({func: callback, scope: scope});
443 					}
444 				}
445 			}
446 
447 			target = callbackList = 0; // Clean memory for IE
448 
449 			return callback;
450 		};
451 
452 		/**
453 		 * Unbinds the specified event by name, name and callback or all events on the target.
454 		 *
455 		 * @method unbind
456 		 * @param {Object} target Target node/window or custom object.
457 		 * @param {String} names Optional event name to unbind.
458 		 * @param {function} callback Optional callback function to unbind.
459 		 * @return {EventUtils} Event utils instance.
460 		 */
461 		self.unbind = function(target, names, callback) {
462 			var id, callbackList, i, ci, name, eventMap;
463 
464 			// Don't bind to text nodes or comments
465 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
466 				return self;
467 			}
468 
469 			// Unbind event or events if the target has the expando
470 			id = target[expando];
471 			if (id) {
472 				eventMap = events[id];
473 
474 				// Specific callback
475 				if (names) {
476 					names = names.split(' ');
477 					i = names.length;
478 					while (i--) {
479 						name = names[i];
480 						callbackList = eventMap[name];
481 
482 						// Unbind the event if it exists in the map
483 						if (callbackList) {
484 							// Remove specified callback
485 							if (callback) {
486 								ci = callbackList.length;
487 								while (ci--) {
488 									if (callbackList[ci].func === callback) {
489 										var nativeHandler = callbackList.nativeHandler;
490 										var fakeName = callbackList.fakeName, capture = callbackList.capture;
491 
492 										// Clone callbackList since unbind inside a callback would otherwise break the handlers loop
493 										callbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1));
494 										callbackList.nativeHandler = nativeHandler;
495 										callbackList.fakeName = fakeName;
496 										callbackList.capture = capture;
497 
498 										eventMap[name] = callbackList;
499 									}
500 								}
501 							}
502 
503 							// Remove all callbacks if there isn't a specified callback or there is no callbacks left
504 							if (!callback || callbackList.length === 0) {
505 								delete eventMap[name];
506 								removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
507 							}
508 						}
509 					}
510 				} else {
511 					// All events for a specific element
512 					for (name in eventMap) {
513 						callbackList = eventMap[name];
514 						removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
515 					}
516 
517 					eventMap = {};
518 				}
519 
520 				// Check if object is empty, if it isn't then we won't remove the expando map
521 				for (name in eventMap) {
522 					return self;
523 				}
524 
525 				// Delete event object
526 				delete events[id];
527 
528 				// Remove expando from target
529 				try {
530 					// IE will fail here since it can't delete properties from window
531 					delete target[expando];
532 				} catch (ex) {
533 					// IE will set it to null
534 					target[expando] = null;
535 				}
536 			}
537 
538 			return self;
539 		};
540 
541 		/**
542 		 * Fires the specified event on the specified target.
543 		 *
544 		 * @method fire
545 		 * @param {Object} target Target node/window or custom object.
546 		 * @param {String} name Event name to fire.
547 		 * @param {Object} args Optional arguments to send to the observers.
548 		 * @return {EventUtils} Event utils instance.
549 		 */
550 		self.fire = function(target, name, args) {
551 			var id;
552 
553 			// Don't bind to text nodes or comments
554 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
555 				return self;
556 			}
557 
558 			// Build event object by patching the args
559 			args = fix(null, args);
560 			args.type = name;
561 			args.target = target;
562 
563 			do {
564 				// Found an expando that means there is listeners to execute
565 				id = target[expando];
566 				if (id) {
567 					executeHandlers(args, id);
568 				}
569 
570 				// Walk up the DOM
571 				target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;
572 			} while (target && !args.isPropagationStopped());
573 
574 			return self;
575 		};
576 
577 		/**
578 		 * Removes all bound event listeners for the specified target. This will also remove any bound
579 		 * listeners to child nodes within that target.
580 		 *
581 		 * @method clean
582 		 * @param {Object} target Target node/window object.
583 		 * @return {EventUtils} Event utils instance.
584 		 */
585 		self.clean = function(target) {
586 			var i, children, unbind = self.unbind;
587 
588 			// Don't bind to text nodes or comments
589 			if (!target || target.nodeType === 3 || target.nodeType === 8) {
590 				return self;
591 			}
592 
593 			// Unbind any element on the specificed target
594 			if (target[expando]) {
595 				unbind(target);
596 			}
597 
598 			// Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children
599 			if (!target.getElementsByTagName) {
600 				target = target.document;
601 			}
602 
603 			// Remove events from each child element
604 			if (target && target.getElementsByTagName) {
605 				unbind(target);
606 
607 				children = target.getElementsByTagName('*');
608 				i = children.length;
609 				while (i--) {
610 					target = children[i];
611 
612 					if (target[expando]) {
613 						unbind(target);
614 					}
615 				}
616 			}
617 
618 			return self;
619 		};
620 
621 		/**
622 		 * Destroys the event object. Call this on IE to remove memory leaks.
623 		 */
624 		self.destroy = function() {
625 			events = {};
626 		};
627 
628 		// Legacy function for canceling events
629 		self.cancel = function(e) {
630 			if (e) {
631 				e.preventDefault();
632 				e.stopImmediatePropagation();
633 			}
634 
635 			return false;
636 		};
637 	}
638 
639 	EventUtils.Event = new EventUtils();
640 	EventUtils.Event.bind(window, 'ready', function() {});
641 
642 	return EventUtils;
643 });
644 
645 // Included from: js/tinymce/classes/dom/Sizzle.js
646 
647 /**
648  * Sizzle.js
649  *
650  * Copyright, Moxiecode Systems AB
651  * Released under LGPL License.
652  *
653  * License: http://www.tinymce.com/license
654  * Contributing: http://www.tinymce.com/contributing
655  *
656  * @ignore-file
657  */
658 
659 /*jshint bitwise:false, expr:true, noempty:false, sub:true, eqnull:true, latedef:false, maxlen:255 */
660 /*eslint dot-notation:0, no-empty:0, no-cond-assign:0, no-unused-expressions:0, new-cap:0, no-nested-ternary:0, func-style:0, no-bitwise: 0 */
661 
662 /*
663  * Sizzle CSS Selector Engine
664  *  Copyright, The Dojo Foundation
665  *  Released under the MIT, BSD, and GPL Licenses.
666  *  More information: http://sizzlejs.com/
667  */
668 define("tinymce/dom/Sizzle", [], function() {
669 var i,
670 	cachedruns,
671 	Expr,
672 	getText,
673 	isXML,
674 	compile,
675 	outermostContext,
676 	recompare,
677 	sortInput,
678 
679 	// Local document vars
680 	setDocument,
681 	document,
682 	docElem,
683 	documentIsHTML,
684 	rbuggyQSA,
685 	rbuggyMatches,
686 	matches,
687 	contains,
688 
689 	// Instance-specific data
690 	expando = "sizzle" + -(new Date()),
691 	preferredDoc = window.document,
692 	support = {},
693 	dirruns = 0,
694 	done = 0,
695 	classCache = createCache(),
696 	tokenCache = createCache(),
697 	compilerCache = createCache(),
698 	hasDuplicate = false,
699 	sortOrder = function() { return 0; },
700 
701 	// General-purpose constants
702 	strundefined = typeof undefined,
703 	MAX_NEGATIVE = 1 << 31,
704 
705 	// Array methods
706 	arr = [],
707 	pop = arr.pop,
708 	push_native = arr.push,
709 	push = arr.push,
710 	slice = arr.slice,
711 	// Use a stripped-down indexOf if we can't use a native one
712 	indexOf = arr.indexOf || function( elem ) {
713 		var i = 0,
714 			len = this.length;
715 		for ( ; i < len; i++ ) {
716 			if ( this[i] === elem ) {
717 				return i;
718 			}
719 		}
720 		return -1;
721 	},
722 
723 
724 	// Regular expressions
725 
726 	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
727 	whitespace = "[\\x20\\t\\r\\n\\f]",
728 	// http://www.w3.org/TR/css3-syntax/#characters
729 	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
730 
731 	// Loosely modeled on CSS identifier characters
732 	// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
733 	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
734 	identifier = characterEncoding.replace( "w", "w#" ),
735 
736 	// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
737 	operators = "([*^$|!~]?=)",
738 	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
739 		"*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
740 
741 	// Prefer arguments quoted,
742 	//   then not containing pseudos/brackets,
743 	//   then attribute selectors/non-parenthetical expressions,
744 	//   then anything else
745 	// These preferences are here to reduce the number of selectors
746 	//   needing tokenize in the PSEUDO preFilter
747 	pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
748 
749 	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
750 	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
751 
752 	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
753 	rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
754 	rpseudo = new RegExp( pseudos ),
755 	ridentifier = new RegExp( "^" + identifier + "$" ),
756 
757 	matchExpr = {
758 		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
759 		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
760 		"NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
761 		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
762 		"ATTR": new RegExp( "^" + attributes ),
763 		"PSEUDO": new RegExp( "^" + pseudos ),
764 		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
765 			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
766 			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
767 		// For use in libraries implementing .is()
768 		// We use this for POS matching in `select`
769 		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
770 			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
771 	},
772 
773 	rsibling = /[\x20\t\r\n\f]*[+~]/,
774 
775 	rnative = /^[^{]+\{\s*\[native code/,
776 
777 	// Easily-parseable/retrievable ID or TAG or CLASS selectors
778 	rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,
779 
780 	rinputs = /^(?:input|select|textarea|button)$/i,
781 	rheader = /^h\d$/i,
782 
783 	rescape = /'|\\/g,
784 	rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
785 
786 	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
787 	runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,
788 	funescape = function( _, escaped ) {
789 		var high = "0x" + escaped - 0x10000;
790 		// NaN means non-codepoint
791 		return high !== high ?
792 			escaped :
793 			// BMP codepoint
794 			high < 0 ?
795 				String.fromCharCode( high + 0x10000 ) :
796 				// Supplemental Plane codepoint (surrogate pair)
797 				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
798 	};
799 
800 // Optimize for push.apply( _, NodeList )
801 try {
802 	push.apply(
803 		(arr = slice.call( preferredDoc.childNodes )),
804 		preferredDoc.childNodes
805 	);
806 	// Support: Android<4.0
807 	// Detect silently failing push.apply
808 	arr[ preferredDoc.childNodes.length ].nodeType;
809 } catch ( e ) {
810 	push = { apply: arr.length ?
811 
812 		// Leverage slice if possible
813 		function( target, els ) {
814 			push_native.apply( target, slice.call(els) );
815 		} :
816 
817 		// Support: IE<9
818 		// Otherwise append directly
819 		function( target, els ) {
820 			var j = target.length,
821 				i = 0;
822 			// Can't trust NodeList.length
823 			while ( (target[j++] = els[i++]) ) {}
824 			target.length = j - 1;
825 		}
826 	};
827 }
828 
829 /**
830  * For feature detection
831  * @param {Function} fn The function to test for native support
832  */
833 function isNative( fn ) {
834 	return rnative.test( fn + "" );
835 }
836 
837 /**
838  * Create key-value caches of limited size
839  * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
840  *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
841  *	deleting the oldest entry
842  */
843 function createCache() {
844 	var cache,
845 		keys = [];
846 
847 	cache = function( key, value ) {
848 		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
849 		if ( keys.push( key += " " ) > Expr.cacheLength ) {
850 			// Only keep the most recent entries
851 			delete cache[ keys.shift() ];
852 		}
853 		cache[ key ] = value;
854 		return value;
855 	};
856 
857 	return cache;
858 }
859 
860 /**
861  * Mark a function for special use by Sizzle
862  * @param {Function} fn The function to mark
863  */
864 function markFunction( fn ) {
865 	fn[ expando ] = true;
866 	return fn;
867 }
868 
869 /**
870  * Support testing using an element
871  * @param {Function} fn Passed the created div and expects a boolean result
872  */
873 function assert( fn ) {
874 	var div = document.createElement("div");
875 
876 	try {
877 		return !!fn( div );
878 	} catch (e) {
879 		return false;
880 	} finally {
881 		// release memory in IE
882 		div = null;
883 	}
884 }
885 
886 function Sizzle( selector, context, results, seed ) {
887 	var match, elem, m, nodeType,
888 		// QSA vars
889 		i, groups, old, nid, newContext, newSelector;
890 
891 	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
892 		setDocument( context );
893 	}
894 
895 	context = context || document;
896 	results = results || [];
897 
898 	if ( !selector || typeof selector !== "string" ) {
899 		return results;
900 	}
901 
902 	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
903 		return [];
904 	}
905 
906 	if ( documentIsHTML && !seed ) {
907 
908 		// Shortcuts
909 		if ( (match = rquickExpr.exec( selector )) ) {
910 			// Speed-up: Sizzle("#ID")
911 			if ( (m = match[1]) ) {
912 				if ( nodeType === 9 ) {
913 					elem = context.getElementById( m );
914 					// Check parentNode to catch when Blackberry 4.6 returns
915 					// nodes that are no longer in the document #6963
916 					if ( elem && elem.parentNode ) {
917 						// Handle the case where IE, Opera, and Webkit return items
918 						// by name instead of ID
919 						if ( elem.id === m ) {
920 							results.push( elem );
921 							return results;
922 						}
923 					} else {
924 						return results;
925 					}
926 				} else {
927 					// Context is not a document
928 					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
929 						contains( context, elem ) && elem.id === m ) {
930 						results.push( elem );
931 						return results;
932 					}
933 				}
934 
935 			// Speed-up: Sizzle("TAG")
936 			} else if ( match[2] ) {
937 				push.apply( results, context.getElementsByTagName( selector ) );
938 				return results;
939 
940 			// Speed-up: Sizzle(".CLASS")
941 			} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
942 				push.apply( results, context.getElementsByClassName( m ) );
943 				return results;
944 			}
945 		}
946 
947 		// QSA path
948 		if ( support.qsa && !rbuggyQSA.test(selector) ) {
949 			old = true;
950 			nid = expando;
951 			newContext = context;
952 			newSelector = nodeType === 9 && selector;
953 
954 			// qSA works strangely on Element-rooted queries
955 			// We can work around this by specifying an extra ID on the root
956 			// and working up from there (Thanks to Andrew Dupont for the technique)
957 			// IE 8 doesn't work on object elements
958 			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
959 				groups = tokenize( selector );
960 
961 				if ( (old = context.getAttribute("id")) ) {
962 					nid = old.replace( rescape, "\\$&" );
963 				} else {
964 					context.setAttribute( "id", nid );
965 				}
966 				nid = "[id='" + nid + "'] ";
967 
968 				i = groups.length;
969 				while ( i-- ) {
970 					groups[i] = nid + toSelector( groups[i] );
971 				}
972 				newContext = rsibling.test( selector ) && context.parentNode || context;
973 				newSelector = groups.join(",");
974 			}
975 
976 			if ( newSelector ) {
977 				try {
978 					push.apply( results,
979 						newContext.querySelectorAll( newSelector )
980 					);
981 					return results;
982 				} catch(qsaError) {
983 				} finally {
984 					if ( !old ) {
985 						context.removeAttribute("id");
986 					}
987 				}
988 			}
989 		}
990 	}
991 
992 	// All others
993 	return select( selector.replace( rtrim, "$1" ), context, results, seed );
994 }
995 
996 /**
997  * Detect xml
998  * @param {Element|Object} elem An element or a document
999  */
1000 isXML = Sizzle.isXML = function( elem ) {
1001 	// documentElement is verified for cases where it doesn't yet exist
1002 	// (such as loading iframes in IE - #4833)
1003 	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
1004 	return documentElement ? documentElement.nodeName !== "HTML" : false;
1005 };
1006 
1007 /**
1008  * Sets document-related variables once based on the current document
1009  * @param {Element|Object} [doc] An element or document object to use to set the document
1010  * @returns {Object} Returns the current document
1011  */
1012 setDocument = Sizzle.setDocument = function( node ) {
1013 	var doc = node ? node.ownerDocument || node : preferredDoc;
1014 
1015 	// If no document and documentElement is available, return
1016 	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
1017 		return document;
1018 	}
1019 
1020 	// Set our document
1021 	document = doc;
1022 	docElem = doc.documentElement;
1023 
1024 	// Support tests
1025 	documentIsHTML = !isXML( doc );
1026 
1027 	// Check if getElementsByTagName("*") returns only elements
1028 	support.getElementsByTagName = assert(function( div ) {
1029 		div.appendChild( doc.createComment("") );
1030 		return !div.getElementsByTagName("*").length;
1031 	});
1032 
1033 	// Check if attributes should be retrieved by attribute nodes
1034 	support.attributes = assert(function( div ) {
1035 		div.innerHTML = "<select></select>";
1036 		var type = typeof div.lastChild.getAttribute("multiple");
1037 		// IE8 returns a string for some attributes even when not present
1038 		return type !== "boolean" && type !== "string";
1039 	});
1040 
1041 	// Check if getElementsByClassName can be trusted
1042 	support.getElementsByClassName = assert(function( div ) {
1043 		// Opera can't find a second classname (in 9.6)
1044 		div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
1045 		if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
1046 			return false;
1047 		}
1048 
1049 		// Safari 3.2 caches class attributes and doesn't catch changes
1050 		div.lastChild.className = "e";
1051 		return div.getElementsByClassName("e").length === 2;
1052 	});
1053 
1054 	// Check if getElementsByName privileges form controls or returns elements by ID
1055 	// If so, assume (for broader support) that getElementById returns elements by name
1056 	support.getByName = assert(function( div ) {
1057 		// Inject content
1058 		div.id = expando + 0;
1059 		// Support: Windows 8 Native Apps
1060 		// Assigning innerHTML with "name" attributes throws uncatchable exceptions
1061 		// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx
1062 		div.appendChild( document.createElement("a") ).setAttribute( "name", expando );
1063 		div.appendChild( document.createElement("i") ).setAttribute( "name", expando );
1064 		docElem.appendChild( div );
1065 
1066 		// Test
1067 		var pass = doc.getElementsByName &&
1068 			// buggy browsers will return fewer than the correct 2
1069 			doc.getElementsByName( expando ).length === 2 +
1070 			// buggy browsers will return more than the correct 0
1071 			doc.getElementsByName( expando + 0 ).length;
1072 
1073 		// Cleanup
1074 		docElem.removeChild( div );
1075 
1076 		return pass;
1077 	});
1078 
1079 	// Support: Webkit<537.32
1080 	// Detached nodes confoundingly follow *each other*
1081 	support.sortDetached = assert(function( div1 ) {
1082 		return div1.compareDocumentPosition &&
1083 			// Should return 1, but Webkit returns 4 (following)
1084 			(div1.compareDocumentPosition( document.createElement("div") ) & 1);
1085 	});
1086 
1087 	// IE6/7 return modified attributes
1088 	Expr.attrHandle = assert(function( div ) {
1089 		div.innerHTML = "<a href='#'></a>";
1090 		return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
1091 			div.firstChild.getAttribute("href") === "#";
1092 	}) ?
1093 		{} :
1094 		{
1095 			"href": function( elem ) {
1096 				return elem.getAttribute( "href", 2 );
1097 			},
1098 			"type": function( elem ) {
1099 				return elem.getAttribute("type");
1100 			}
1101 		};
1102 
1103 	// ID find and filter
1104 	if ( support.getByName ) {
1105 		Expr.find["ID"] = function( id, context ) {
1106 			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
1107 				var m = context.getElementById( id );
1108 				// Check parentNode to catch when Blackberry 4.6 returns
1109 				// nodes that are no longer in the document #6963
1110 				return m && m.parentNode ? [m] : [];
1111 			}
1112 		};
1113 		Expr.filter["ID"] = function( id ) {
1114 			var attrId = id.replace( runescape, funescape );
1115 			return function( elem ) {
1116 				return elem.getAttribute("id") === attrId;
1117 			};
1118 		};
1119 	} else {
1120 		Expr.find["ID"] = function( id, context ) {
1121 			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
1122 				var m = context.getElementById( id );
1123 
1124 				return m ?
1125 					m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
1126 						[m] :
1127 						undefined :
1128 					[];
1129 			}
1130 		};
1131 		Expr.filter["ID"] =  function( id ) {
1132 			var attrId = id.replace( runescape, funescape );
1133 			return function( elem ) {
1134 				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
1135 				return node && node.value === attrId;
1136 			};
1137 		};
1138 	}
1139 
1140 	// Tag
1141 	Expr.find["TAG"] = support.getElementsByTagName ?
1142 		function( tag, context ) {
1143 			if ( typeof context.getElementsByTagName !== strundefined ) {
1144 				return context.getElementsByTagName( tag );
1145 			}
1146 		} :
1147 		function( tag, context ) {
1148 			var elem,
1149 				tmp = [],
1150 				i = 0,
1151 				results = context.getElementsByTagName( tag );
1152 
1153 			// Filter out possible comments
1154 			if ( tag === "*" ) {
1155 				while ( (elem = results[i++]) ) {
1156 					if ( elem.nodeType === 1 ) {
1157 						tmp.push( elem );
1158 					}
1159 				}
1160 
1161 				return tmp;
1162 			}
1163 			return results;
1164 		};
1165 
1166 	// Name
1167 	Expr.find["NAME"] = support.getByName && function( tag, context ) {
1168 		if ( typeof context.getElementsByName !== strundefined ) {
1169 			return context.getElementsByName( name );
1170 		}
1171 	};
1172 
1173 	// Class
1174 	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
1175 		if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
1176 			return context.getElementsByClassName( className );
1177 		}
1178 	};
1179 
1180 	// QSA and matchesSelector support
1181 
1182 	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
1183 	rbuggyMatches = [];
1184 
1185 	// qSa(:focus) reports false when true (Chrome 21),
1186 	// no need to also add to buggyMatches since matches checks buggyQSA
1187 	// A support test would require too much code (would include document ready)
1188 	rbuggyQSA = [ ":focus" ];
1189 
1190 	if ( (support.qsa = isNative(doc.querySelectorAll)) ) {
1191 		// Build QSA regex
1192 		// Regex strategy adopted from Diego Perini
1193 		assert(function( div ) {
1194 			// Select is set to empty string on purpose
1195 			// This is to test IE's treatment of not explicitly
1196 			// setting a boolean content attribute,
1197 			// since its presence should be enough
1198 			// http://bugs.jquery.com/ticket/12359
1199 			div.innerHTML = "<select><option selected=''></option></select>";
1200 
1201 			// IE8 - Some boolean attributes are not treated correctly
1202 			if ( !div.querySelectorAll("[selected]").length ) {
1203 				rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
1204 			}
1205 
1206 			// Webkit/Opera - :checked should return selected option elements
1207 			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
1208 			// IE8 throws error here and will not see later tests
1209 			if ( !div.querySelectorAll(":checked").length ) {
1210 				rbuggyQSA.push(":checked");
1211 			}
1212 		});
1213 
1214 		assert(function( div ) {
1215 
1216 			// Opera 10-12/IE8 - ^= $= *= and empty values
1217 			// Should not select anything
1218 			div.innerHTML = "<input type='hidden' i=''/>";
1219 			if ( div.querySelectorAll("[i^='']").length ) {
1220 				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
1221 			}
1222 
1223 			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
1224 			// IE8 throws error here and will not see later tests
1225 			if ( !div.querySelectorAll(":enabled").length ) {
1226 				rbuggyQSA.push( ":enabled", ":disabled" );
1227 			}
1228 
1229 			// Opera 10-11 does not throw on post-comma invalid pseudos
1230 			div.querySelectorAll("*,:x");
1231 			rbuggyQSA.push(",.*:");
1232 		});
1233 	}
1234 
1235 	if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector ||
1236 		docElem.mozMatchesSelector ||
1237 		docElem.webkitMatchesSelector ||
1238 		docElem.oMatchesSelector ||
1239 		docElem.msMatchesSelector) )) ) {
1240 
1241 		assert(function( div ) {
1242 			// Check to see if it's possible to do matchesSelector
1243 			// on a disconnected node (IE 9)
1244 			support.disconnectedMatch = matches.call( div, "div" );
1245 
1246 			// This should fail with an exception
1247 			// Gecko does not error, returns false instead
1248 			matches.call( div, "[s!='']:x" );
1249 			rbuggyMatches.push( "!=", pseudos );
1250 		});
1251 	}
1252 
1253 	rbuggyQSA = new RegExp( rbuggyQSA.join("|") );
1254 	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
1255 
1256 	// Element contains another
1257 	// Purposefully does not implement inclusive descendant
1258 	// As in, an element does not contain itself
1259 	contains = isNative(docElem.contains) || docElem.compareDocumentPosition ?
1260 		function( a, b ) {
1261 			var adown = a.nodeType === 9 ? a.documentElement : a,
1262 				bup = b && b.parentNode;
1263 			return a === bup || !!( bup && bup.nodeType === 1 && (
1264 				adown.contains ?
1265 					adown.contains( bup ) :
1266 					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
1267 			));
1268 		} :
1269 		function( a, b ) {
1270 			if ( b ) {
1271 				while ( (b = b.parentNode) ) {
1272 					if ( b === a ) {
1273 						return true;
1274 					}
1275 				}
1276 			}
1277 			return false;
1278 		};
1279 
1280 	// Document order sorting
1281 	sortOrder = docElem.compareDocumentPosition ?
1282 	function( a, b ) {
1283 
1284 		// Flag for duplicate removal
1285 		if ( a === b ) {
1286 			hasDuplicate = true;
1287 			return 0;
1288 		}
1289 
1290 		var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
1291 
1292 		if ( compare ) {
1293 			// Disconnected nodes
1294 			if ( compare & 1 ||
1295 				(recompare && b.compareDocumentPosition( a ) === compare) ) {
1296 
1297 				// Choose the first element that is related to our preferred document
1298 				if ( a === doc || contains(preferredDoc, a) ) {
1299 					return -1;
1300 				}
1301 				if ( b === doc || contains(preferredDoc, b) ) {
1302 					return 1;
1303 				}
1304 
1305 				// Maintain original order
1306 				return sortInput ?
1307 					( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
1308 					0;
1309 			}
1310 
1311 			return compare & 4 ? -1 : 1;
1312 		}
1313 
1314 		// Not directly comparable, sort on existence of method
1315 		return a.compareDocumentPosition ? -1 : 1;
1316 	} :
1317 	function( a, b ) {
1318 		var cur,
1319 			i = 0,
1320 			aup = a.parentNode,
1321 			bup = b.parentNode,
1322 			ap = [ a ],
1323 			bp = [ b ];
1324 
1325 		// Exit early if the nodes are identical
1326 		if ( a === b ) {
1327 			hasDuplicate = true;
1328 			return 0;
1329 
1330 		// Parentless nodes are either documents or disconnected
1331 		} else if ( !aup || !bup ) {
1332 			return a === doc ? -1 :
1333 				b === doc ? 1 :
1334 				aup ? -1 :
1335 				bup ? 1 :
1336 				0;
1337 
1338 		// If the nodes are siblings, we can do a quick check
1339 		} else if ( aup === bup ) {
1340 			return siblingCheck( a, b );
1341 		}
1342 
1343 		// Otherwise we need full lists of their ancestors for comparison
1344 		cur = a;
1345 		while ( (cur = cur.parentNode) ) {
1346 			ap.unshift( cur );
1347 		}
1348 		cur = b;
1349 		while ( (cur = cur.parentNode) ) {
1350 			bp.unshift( cur );
1351 		}
1352 
1353 		// Walk down the tree looking for a discrepancy
1354 		while ( ap[i] === bp[i] ) {
1355 			i++;
1356 		}
1357 
1358 		return i ?
1359 			// Do a sibling check if the nodes have a common ancestor
1360 			siblingCheck( ap[i], bp[i] ) :
1361 
1362 			// Otherwise nodes in our document sort first
1363 			ap[i] === preferredDoc ? -1 :
1364 			bp[i] === preferredDoc ? 1 :
1365 			0;
1366 	};
1367 
1368 	return document;
1369 };
1370 
1371 Sizzle.matches = function( expr, elements ) {
1372 	return Sizzle( expr, null, null, elements );
1373 };
1374 
1375 Sizzle.matchesSelector = function( elem, expr ) {
1376 	// Set document vars if needed
1377 	if ( ( elem.ownerDocument || elem ) !== document ) {
1378 		setDocument( elem );
1379 	}
1380 
1381 	// Make sure that attribute selectors are quoted
1382 	expr = expr.replace( rattributeQuotes, "='$1']" );
1383 
1384 	// rbuggyQSA always contains :focus, so no need for an existence check
1385 	if ( support.matchesSelector && documentIsHTML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) {
1386 		try {
1387 			var ret = matches.call( elem, expr );
1388 
1389 			// IE 9's matchesSelector returns false on disconnected nodes
1390 			if ( ret || support.disconnectedMatch ||
1391 					// As well, disconnected nodes are said to be in a document
1392 					// fragment in IE 9
1393 					elem.document && elem.document.nodeType !== 11 ) {
1394 				return ret;
1395 			}
1396 		} catch(e) {}
1397 	}
1398 
1399 	return Sizzle( expr, document, null, [elem] ).length > 0;
1400 };
1401 
1402 Sizzle.contains = function( context, elem ) {
1403 	// Set document vars if needed
1404 	if ( ( context.ownerDocument || context ) !== document ) {
1405 		setDocument( context );
1406 	}
1407 	return contains( context, elem );
1408 };
1409 
1410 Sizzle.attr = function( elem, name ) {
1411 	var val;
1412 
1413 	// Set document vars if needed
1414 	if ( ( elem.ownerDocument || elem ) !== document ) {
1415 		setDocument( elem );
1416 	}
1417 
1418 	if ( documentIsHTML ) {
1419 		name = name.toLowerCase();
1420 	}
1421 	if ( (val = Expr.attrHandle[ name ]) ) {
1422 		return val( elem );
1423 	}
1424 	if ( !documentIsHTML || support.attributes ) {
1425 		return elem.getAttribute( name );
1426 	}
1427 	return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ?
1428 		name :
1429 		val && val.specified ? val.value : null;
1430 };
1431 
1432 Sizzle.error = function( msg ) {
1433 	throw new Error( "Syntax error, unrecognized expression: " + msg );
1434 };
1435 
1436 // Document sorting and removing duplicates
1437 Sizzle.uniqueSort = function( results ) {
1438 	var elem,
1439 		duplicates = [],
1440 		j = 0,
1441 		i = 0;
1442 
1443 	// Unless we *know* we can detect duplicates, assume their presence
1444 	hasDuplicate = !support.detectDuplicates;
1445 	// Compensate for sort limitations
1446 	recompare = !support.sortDetached;
1447 	sortInput = !support.sortStable && results.slice( 0 );
1448 	results.sort( sortOrder );
1449 
1450 	if ( hasDuplicate ) {
1451 		while ( (elem = results[i++]) ) {
1452 			if ( elem === results[ i ] ) {
1453 				j = duplicates.push( i );
1454 			}
1455 		}
1456 		while ( j-- ) {
1457 			results.splice( duplicates[ j ], 1 );
1458 		}
1459 	}
1460 
1461 	return results;
1462 };
1463 
1464 /**
1465  * Checks document order of two siblings
1466  * @param {Element} a
1467  * @param {Element} b
1468  * @returns Returns -1 if a precedes b, 1 if a follows b
1469  */
1470 function siblingCheck( a, b ) {
1471 	var cur = b && a,
1472 		diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE );
1473 
1474 	// Use IE sourceIndex if available on both nodes
1475 	if ( diff ) {
1476 		return diff;
1477 	}
1478 
1479 	// Check if b follows a
1480 	if ( cur ) {
1481 		while ( (cur = cur.nextSibling) ) {
1482 			if ( cur === b ) {
1483 				return -1;
1484 			}
1485 		}
1486 	}
1487 
1488 	return a ? 1 : -1;
1489 }
1490 
1491 // Returns a function to use in pseudos for input types
1492 function createInputPseudo( type ) {
1493 	return function( elem ) {
1494 		var name = elem.nodeName.toLowerCase();
1495 		return name === "input" && elem.type === type;
1496 	};
1497 }
1498 
1499 // Returns a function to use in pseudos for buttons
1500 function createButtonPseudo( type ) {
1501 	return function( elem ) {
1502 		var name = elem.nodeName.toLowerCase();
1503 		return (name === "input" || name === "button") && elem.type === type;
1504 	};
1505 }
1506 
1507 // Returns a function to use in pseudos for positionals
1508 function createPositionalPseudo( fn ) {
1509 	return markFunction(function( argument ) {
1510 		argument = +argument;
1511 		return markFunction(function( seed, matches ) {
1512 			var j,
1513 				matchIndexes = fn( [], seed.length, argument ),
1514 				i = matchIndexes.length;
1515 
1516 			// Match elements found at the specified indexes
1517 			while ( i-- ) {
1518 				if ( seed[ (j = matchIndexes[i]) ] ) {
1519 					seed[j] = !(matches[j] = seed[j]);
1520 				}
1521 			}
1522 		});
1523 	});
1524 }
1525 
1526 /**
1527  * Utility function for retrieving the text value of an array of DOM nodes
1528  * @param {Array|Element} elem
1529  */
1530 getText = Sizzle.getText = function( elem ) {
1531 	var node,
1532 		ret = "",
1533 		i = 0,
1534 		nodeType = elem.nodeType;
1535 
1536 	if ( !nodeType ) {
1537 		// If no nodeType, this is expected to be an array
1538 		for ( ; (node = elem[i]); i++ ) {
1539 			// Do not traverse comment nodes
1540 			ret += getText( node );
1541 		}
1542 	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
1543 		// Use textContent for elements
1544 		// innerText usage removed for consistency of new lines (see #11153)
1545 		if ( typeof elem.textContent === "string" ) {
1546 			return elem.textContent;
1547 		} else {
1548 			// Traverse its children
1549 			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
1550 				ret += getText( elem );
1551 			}
1552 		}
1553 	} else if ( nodeType === 3 || nodeType === 4 ) {
1554 		return elem.nodeValue;
1555 	}
1556 	// Do not include comment or processing instruction nodes
1557 
1558 	return ret;
1559 };
1560 
1561 Expr = Sizzle.selectors = {
1562 
1563 	// Can be adjusted by the user
1564 	cacheLength: 50,
1565 
1566 	createPseudo: markFunction,
1567 
1568 	match: matchExpr,
1569 
1570 	find: {},
1571 
1572 	relative: {
1573 		">": { dir: "parentNode", first: true },
1574 		" ": { dir: "parentNode" },
1575 		"+": { dir: "previousSibling", first: true },
1576 		"~": { dir: "previousSibling" }
1577 	},
1578 
1579 	preFilter: {
1580 		"ATTR": function( match ) {
1581 			match[1] = match[1].replace( runescape, funescape );
1582 
1583 			// Move the given value to match[3] whether quoted or unquoted
1584 			match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
1585 
1586 			if ( match[2] === "~=" ) {
1587 				match[3] = " " + match[3] + " ";
1588 			}
1589 
1590 			return match.slice( 0, 4 );
1591 		},
1592 
1593 		"CHILD": function( match ) {
1594 			/* matches from matchExpr["CHILD"]
1595 				1 type (only|nth|...)
1596 				2 what (child|of-type)
1597 				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
1598 				4 xn-component of xn+y argument ([+-]?\d*n|)
1599 				5 sign of xn-component
1600 				6 x of xn-component
1601 				7 sign of y-component
1602 				8 y of y-component
1603 			*/
1604 			match[1] = match[1].toLowerCase();
1605 
1606 			if ( match[1].slice( 0, 3 ) === "nth" ) {
1607 				// nth-* requires argument
1608 				if ( !match[3] ) {
1609 					Sizzle.error( match[0] );
1610 				}
1611 
1612 				// numeric x and y parameters for Expr.filter.CHILD
1613 				// remember that false/true cast respectively to 0/1
1614 				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
1615 				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
1616 
1617 			// other types prohibit arguments
1618 			} else if ( match[3] ) {
1619 				Sizzle.error( match[0] );
1620 			}
1621 
1622 			return match;
1623 		},
1624 
1625 		"PSEUDO": function( match ) {
1626 			var excess,
1627 				unquoted = !match[5] && match[2];
1628 
1629 			if ( matchExpr["CHILD"].test( match[0] ) ) {
1630 				return null;
1631 			}
1632 
1633 			// Accept quoted arguments as-is
1634 			if ( match[4] ) {
1635 				match[2] = match[4];
1636 
1637 			// Strip excess characters from unquoted arguments
1638 			} else if ( unquoted && rpseudo.test( unquoted ) &&
1639 				// Get excess from tokenize (recursively)
1640 				(excess = tokenize( unquoted, true )) &&
1641 				// advance to the next closing parenthesis
1642 				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
1643 
1644 				// excess is a negative index
1645 				match[0] = match[0].slice( 0, excess );
1646 				match[2] = unquoted.slice( 0, excess );
1647 			}
1648 
1649 			// Return only captures needed by the pseudo filter method (type and argument)
1650 			return match.slice( 0, 3 );
1651 		}
1652 	},
1653 
1654 	filter: {
1655 
1656 		"TAG": function( nodeName ) {
1657 			if ( nodeName === "*" ) {
1658 				return function() { return true; };
1659 			}
1660 
1661 			nodeName = nodeName.replace( runescape, funescape ).toLowerCase();
1662 			return function( elem ) {
1663 				return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
1664 			};
1665 		},
1666 
1667 		"CLASS": function( className ) {
1668 			var pattern = classCache[ className + " " ];
1669 
1670 			return pattern ||
1671 				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
1672 				classCache( className, function( elem ) {
1673 					return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
1674 				});
1675 		},
1676 
1677 		"ATTR": function( name, operator, check ) {
1678 			return function( elem ) {
1679 				var result = Sizzle.attr( elem, name );
1680 
1681 				if ( result == null ) {
1682 					return operator === "!=";
1683 				}
1684 				if ( !operator ) {
1685 					return true;
1686 				}
1687 
1688 				result += "";
1689 
1690 				return operator === "=" ? result === check :
1691 					operator === "!=" ? result !== check :
1692 					operator === "^=" ? check && result.indexOf( check ) === 0 :
1693 					operator === "*=" ? check && result.indexOf( check ) > -1 :
1694 					operator === "$=" ? check && result.slice( -check.length ) === check :
1695 					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
1696 					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
1697 					false;
1698 			};
1699 		},
1700 
1701 		"CHILD": function( type, what, argument, first, last ) {
1702 			var simple = type.slice( 0, 3 ) !== "nth",
1703 				forward = type.slice( -4 ) !== "last",
1704 				ofType = what === "of-type";
1705 
1706 			return first === 1 && last === 0 ?
1707 
1708 				// Shortcut for :nth-*(n)
1709 				function( elem ) {
1710 					return !!elem.parentNode;
1711 				} :
1712 
1713 				function( elem, context, xml ) {
1714 					var cache, outerCache, node, diff, nodeIndex, start,
1715 						dir = simple !== forward ? "nextSibling" : "previousSibling",
1716 						parent = elem.parentNode,
1717 						name = ofType && elem.nodeName.toLowerCase(),
1718 						useCache = !xml && !ofType;
1719 
1720 					if ( parent ) {
1721 
1722 						// :(first|last|only)-(child|of-type)
1723 						if ( simple ) {
1724 							while ( dir ) {
1725 								node = elem;
1726 								while ( (node = node[ dir ]) ) {
1727 									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
1728 										return false;
1729 									}
1730 								}
1731 								// Reverse direction for :only-* (if we haven't yet done so)
1732 								start = dir = type === "only" && !start && "nextSibling";
1733 							}
1734 							return true;
1735 						}
1736 
1737 						start = [ forward ? parent.firstChild : parent.lastChild ];
1738 
1739 						// non-xml :nth-child(...) stores cache data on `parent`
1740 						if ( forward && useCache ) {
1741 							// Seek `elem` from a previously-cached index
1742 							outerCache = parent[ expando ] || (parent[ expando ] = {});
1743 							cache = outerCache[ type ] || [];
1744 							nodeIndex = cache[0] === dirruns && cache[1];
1745 							diff = cache[0] === dirruns && cache[2];
1746 							node = nodeIndex && parent.childNodes[ nodeIndex ];
1747 
1748 							while ( (node = ++nodeIndex && node && node[ dir ] ||
1749 
1750 								// Fallback to seeking `elem` from the start
1751 								(diff = nodeIndex = 0) || start.pop()) ) {
1752 
1753 								// When found, cache indexes on `parent` and break
1754 								if ( node.nodeType === 1 && ++diff && node === elem ) {
1755 									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
1756 									break;
1757 								}
1758 							}
1759 
1760 						// Use previously-cached element index if available
1761 						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
1762 							diff = cache[1];
1763 
1764 						// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
1765 						} else {
1766 							// Use the same loop as above to seek `elem` from the start
1767 							while ( (node = ++nodeIndex && node && node[ dir ] ||
1768 								(diff = nodeIndex = 0) || start.pop()) ) {
1769 
1770 								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
1771 									// Cache the index of each encountered element
1772 									if ( useCache ) {
1773 										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
1774 									}
1775 
1776 									if ( node === elem ) {
1777 										break;
1778 									}
1779 								}
1780 							}
1781 						}
1782 
1783 						// Incorporate the offset, then check against cycle size
1784 						diff -= last;
1785 						return diff === first || ( diff % first === 0 && diff / first >= 0 );
1786 					}
1787 				};
1788 		},
1789 
1790 		"PSEUDO": function( pseudo, argument ) {
1791 			// pseudo-class names are case-insensitive
1792 			// http://www.w3.org/TR/selectors/#pseudo-classes
1793 			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
1794 			// Remember that setFilters inherits from pseudos
1795 			var args,
1796 				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
1797 					Sizzle.error( "unsupported pseudo: " + pseudo );
1798 
1799 			// The user may use createPseudo to indicate that
1800 			// arguments are needed to create the filter function
1801 			// just as Sizzle does
1802 			if ( fn[ expando ] ) {
1803 				return fn( argument );
1804 			}
1805 
1806 			// But maintain support for old signatures
1807 			if ( fn.length > 1 ) {
1808 				args = [ pseudo, pseudo, "", argument ];
1809 				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
1810 					markFunction(function( seed, matches ) {
1811 						var idx,
1812 							matched = fn( seed, argument ),
1813 							i = matched.length;
1814 						while ( i-- ) {
1815 							idx = indexOf.call( seed, matched[i] );
1816 							seed[ idx ] = !( matches[ idx ] = matched[i] );
1817 						}
1818 					}) :
1819 					function( elem ) {
1820 						return fn( elem, 0, args );
1821 					};
1822 			}
1823 
1824 			return fn;
1825 		}
1826 	},
1827 
1828 	pseudos: {
1829 		// Potentially complex pseudos
1830 		"not": markFunction(function( selector ) {
1831 			// Trim the selector passed to compile
1832 			// to avoid treating leading and trailing
1833 			// spaces as combinators
1834 			var input = [],
1835 				results = [],
1836 				matcher = compile( selector.replace( rtrim, "$1" ) );
1837 
1838 			return matcher[ expando ] ?
1839 				markFunction(function( seed, matches, context, xml ) {
1840 					var elem,
1841 						unmatched = matcher( seed, null, xml, [] ),
1842 						i = seed.length;
1843 
1844 					// Match elements unmatched by `matcher`
1845 					while ( i-- ) {
1846 						if ( (elem = unmatched[i]) ) {
1847 							seed[i] = !(matches[i] = elem);
1848 						}
1849 					}
1850 				}) :
1851 				function( elem, context, xml ) {
1852 					input[0] = elem;
1853 					matcher( input, null, xml, results );
1854 					return !results.pop();
1855 				};
1856 		}),
1857 
1858 		"has": markFunction(function( selector ) {
1859 			return function( elem ) {
1860 				return Sizzle( selector, elem ).length > 0;
1861 			};
1862 		}),
1863 
1864 		"contains": markFunction(function( text ) {
1865 			return function( elem ) {
1866 				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
1867 			};
1868 		}),
1869 
1870 		// "Whether an element is represented by a :lang() selector
1871 		// is based solely on the element's language value
1872 		// being equal to the identifier C,
1873 		// or beginning with the identifier C immediately followed by "-".
1874 		// The matching of C against the element's language value is performed case-insensitively.
1875 		// The identifier C does not have to be a valid language name."
1876 		// http://www.w3.org/TR/selectors/#lang-pseudo
1877 		"lang": markFunction( function( lang ) {
1878 			// lang value must be a valid identifier
1879 			if ( !ridentifier.test(lang || "") ) {
1880 				Sizzle.error( "unsupported lang: " + lang );
1881 			}
1882 			lang = lang.replace( runescape, funescape ).toLowerCase();
1883 			return function( elem ) {
1884 				var elemLang;
1885 				do {
1886 					if ( (elemLang = documentIsHTML ?
1887 						elem.lang :
1888 						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
1889 
1890 						elemLang = elemLang.toLowerCase();
1891 						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
1892 					}
1893 				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
1894 				return false;
1895 			};
1896 		}),
1897 
1898 		// Miscellaneous
1899 		"target": function( elem ) {
1900 			var hash = window.location && window.location.hash;
1901 			return hash && hash.slice( 1 ) === elem.id;
1902 		},
1903 
1904 		"root": function( elem ) {
1905 			return elem === docElem;
1906 		},
1907 
1908 		"focus": function( elem ) {
1909 			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
1910 		},
1911 
1912 		// Boolean properties
1913 		"enabled": function( elem ) {
1914 			return elem.disabled === false;
1915 		},
1916 
1917 		"disabled": function( elem ) {
1918 			return elem.disabled === true;
1919 		},
1920 
1921 		"checked": function( elem ) {
1922 			// In CSS3, :checked should return both checked and selected elements
1923 			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
1924 			var nodeName = elem.nodeName.toLowerCase();
1925 			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
1926 		},
1927 
1928 		"selected": function( elem ) {
1929 			// Accessing this property makes selected-by-default
1930 			// options in Safari work properly
1931 			if ( elem.parentNode ) {
1932 				elem.parentNode.selectedIndex;
1933 			}
1934 
1935 			return elem.selected === true;
1936 		},
1937 
1938 		// Contents
1939 		"empty": function( elem ) {
1940 			// http://www.w3.org/TR/selectors/#empty-pseudo
1941 			// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
1942 			//   not comment, processing instructions, or others
1943 			// Thanks to Diego Perini for the nodeName shortcut
1944 			//   Greater than "@" means alpha characters (specifically not starting with "#" or "?")
1945 			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
1946 				if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
1947 					return false;
1948 				}
1949 			}
1950 			return true;
1951 		},
1952 
1953 		"parent": function( elem ) {
1954 			return !Expr.pseudos["empty"]( elem );
1955 		},
1956 
1957 		// Element/input types
1958 		"header": function( elem ) {
1959 			return rheader.test( elem.nodeName );
1960 		},
1961 
1962 		"input": function( elem ) {
1963 			return rinputs.test( elem.nodeName );
1964 		},
1965 
1966 		"button": function( elem ) {
1967 			var name = elem.nodeName.toLowerCase();
1968 			return name === "input" && elem.type === "button" || name === "button";
1969 		},
1970 
1971 		"text": function( elem ) {
1972 			var attr;
1973 			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
1974 			// use getAttribute instead to test this case
1975 			return elem.nodeName.toLowerCase() === "input" &&
1976 				elem.type === "text" &&
1977 				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
1978 		},
1979 
1980 		// Position-in-collection
1981 		"first": createPositionalPseudo(function() {
1982 			return [ 0 ];
1983 		}),
1984 
1985 		"last": createPositionalPseudo(function( matchIndexes, length ) {
1986 			return [ length - 1 ];
1987 		}),
1988 
1989 		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
1990 			return [ argument < 0 ? argument + length : argument ];
1991 		}),
1992 
1993 		"even": createPositionalPseudo(function( matchIndexes, length ) {
1994 			var i = 0;
1995 			for ( ; i < length; i += 2 ) {
1996 				matchIndexes.push( i );
1997 			}
1998 			return matchIndexes;
1999 		}),
2000 
2001 		"odd": createPositionalPseudo(function( matchIndexes, length ) {
2002 			var i = 1;
2003 			for ( ; i < length; i += 2 ) {
2004 				matchIndexes.push( i );
2005 			}
2006 			return matchIndexes;
2007 		}),
2008 
2009 		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
2010 			var i = argument < 0 ? argument + length : argument;
2011 			for ( ; --i >= 0; ) {
2012 				matchIndexes.push( i );
2013 			}
2014 			return matchIndexes;
2015 		}),
2016 
2017 		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
2018 			var i = argument < 0 ? argument + length : argument;
2019 			for ( ; ++i < length; ) {
2020 				matchIndexes.push( i );
2021 			}
2022 			return matchIndexes;
2023 		})
2024 	}
2025 };
2026 
2027 // Add button/input type pseudos
2028 for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
2029 	Expr.pseudos[ i ] = createInputPseudo( i );
2030 }
2031 for ( i in { submit: true, reset: true } ) {
2032 	Expr.pseudos[ i ] = createButtonPseudo( i );
2033 }
2034 
2035 function tokenize( selector, parseOnly ) {
2036 	var matched, match, tokens, type,
2037 		soFar, groups, preFilters,
2038 		cached = tokenCache[ selector + " " ];
2039 
2040 	if ( cached ) {
2041 		return parseOnly ? 0 : cached.slice( 0 );
2042 	}
2043 
2044 	soFar = selector;
2045 	groups = [];
2046 	preFilters = Expr.preFilter;
2047 
2048 	while ( soFar ) {
2049 
2050 		// Comma and first run
2051 		if ( !matched || (match = rcomma.exec( soFar )) ) {
2052 			if ( match ) {
2053 				// Don't consume trailing commas as valid
2054 				soFar = soFar.slice( match[0].length ) || soFar;
2055 			}
2056 			groups.push( tokens = [] );
2057 		}
2058 
2059 		matched = false;
2060 
2061 		// Combinators
2062 		if ( (match = rcombinators.exec( soFar )) ) {
2063 			matched = match.shift();
2064 			tokens.push( {
2065 				value: matched,
2066 				// Cast descendant combinators to space
2067 				type: match[0].replace( rtrim, " " )
2068 			} );
2069 			soFar = soFar.slice( matched.length );
2070 		}
2071 
2072 		// Filters
2073 		for ( type in Expr.filter ) {
2074 			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
2075 				(match = preFilters[ type ]( match ))) ) {
2076 				matched = match.shift();
2077 				tokens.push( {
2078 					value: matched,
2079 					type: type,
2080 					matches: match
2081 				} );
2082 				soFar = soFar.slice( matched.length );
2083 			}
2084 		}
2085 
2086 		if ( !matched ) {
2087 			break;
2088 		}
2089 	}
2090 
2091 	// Return the length of the invalid excess
2092 	// if we're just parsing
2093 	// Otherwise, throw an error or return tokens
2094 	return parseOnly ?
2095 		soFar.length :
2096 		soFar ?
2097 			Sizzle.error( selector ) :
2098 			// Cache the tokens
2099 			tokenCache( selector, groups ).slice( 0 );
2100 }
2101 
2102 function toSelector( tokens ) {
2103 	var i = 0,
2104 		len = tokens.length,
2105 		selector = "";
2106 	for ( ; i < len; i++ ) {
2107 		selector += tokens[i].value;
2108 	}
2109 	return selector;
2110 }
2111 
2112 function addCombinator( matcher, combinator, base ) {
2113 	var dir = combinator.dir,
2114 		checkNonElements = base && dir === "parentNode",
2115 		doneName = done++;
2116 
2117 	return combinator.first ?
2118 		// Check against closest ancestor/preceding element
2119 		function( elem, context, xml ) {
2120 			while ( (elem = elem[ dir ]) ) {
2121 				if ( elem.nodeType === 1 || checkNonElements ) {
2122 					return matcher( elem, context, xml );
2123 				}
2124 			}
2125 		} :
2126 
2127 		// Check against all ancestor/preceding elements
2128 		function( elem, context, xml ) {
2129 			var data, cache, outerCache,
2130 				dirkey = dirruns + " " + doneName;
2131 
2132 			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
2133 			if ( xml ) {
2134 				while ( (elem = elem[ dir ]) ) {
2135 					if ( elem.nodeType === 1 || checkNonElements ) {
2136 						if ( matcher( elem, context, xml ) ) {
2137 							return true;
2138 						}
2139 					}
2140 				}
2141 			} else {
2142 				while ( (elem = elem[ dir ]) ) {
2143 					if ( elem.nodeType === 1 || checkNonElements ) {
2144 						outerCache = elem[ expando ] || (elem[ expando ] = {});
2145 						if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
2146 							if ( (data = cache[1]) === true || data === cachedruns ) {
2147 								return data === true;
2148 							}
2149 						} else {
2150 							cache = outerCache[ dir ] = [ dirkey ];
2151 							cache[1] = matcher( elem, context, xml ) || cachedruns;
2152 							if ( cache[1] === true ) {
2153 								return true;
2154 							}
2155 						}
2156 					}
2157 				}
2158 			}
2159 		};
2160 }
2161 
2162 function elementMatcher( matchers ) {
2163 	return matchers.length > 1 ?
2164 		function( elem, context, xml ) {
2165 			var i = matchers.length;
2166 			while ( i-- ) {
2167 				if ( !matchers[i]( elem, context, xml ) ) {
2168 					return false;
2169 				}
2170 			}
2171 			return true;
2172 		} :
2173 		matchers[0];
2174 }
2175 
2176 function condense( unmatched, map, filter, context, xml ) {
2177 	var elem,
2178 		newUnmatched = [],
2179 		i = 0,
2180 		len = unmatched.length,
2181 		mapped = map != null;
2182 
2183 	for ( ; i < len; i++ ) {
2184 		if ( (elem = unmatched[i]) ) {
2185 			if ( !filter || filter( elem, context, xml ) ) {
2186 				newUnmatched.push( elem );
2187 				if ( mapped ) {
2188 					map.push( i );
2189 				}
2190 			}
2191 		}
2192 	}
2193 
2194 	return newUnmatched;
2195 }
2196 
2197 function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
2198 	if ( postFilter && !postFilter[ expando ] ) {
2199 		postFilter = setMatcher( postFilter );
2200 	}
2201 	if ( postFinder && !postFinder[ expando ] ) {
2202 		postFinder = setMatcher( postFinder, postSelector );
2203 	}
2204 	return markFunction(function( seed, results, context, xml ) {
2205 		var temp, i, elem,
2206 			preMap = [],
2207 			postMap = [],
2208 			preexisting = results.length,
2209 
2210 			// Get initial elements from seed or context
2211 			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
2212 
2213 			// Prefilter to get matcher input, preserving a map for seed-results synchronization
2214 			matcherIn = preFilter && ( seed || !selector ) ?
2215 				condense( elems, preMap, preFilter, context, xml ) :
2216 				elems,
2217 
2218 			matcherOut = matcher ?
2219 				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
2220 				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
2221 
2222 					// ...intermediate processing is necessary
2223 					[] :
2224 
2225 					// ...otherwise use results directly
2226 					results :
2227 				matcherIn;
2228 
2229 		// Find primary matches
2230 		if ( matcher ) {
2231 			matcher( matcherIn, matcherOut, context, xml );
2232 		}
2233 
2234 		// Apply postFilter
2235 		if ( postFilter ) {
2236 			temp = condense( matcherOut, postMap );
2237 			postFilter( temp, [], context, xml );
2238 
2239 			// Un-match failing elements by moving them back to matcherIn
2240 			i = temp.length;
2241 			while ( i-- ) {
2242 				if ( (elem = temp[i]) ) {
2243 					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
2244 				}
2245 			}
2246 		}
2247 
2248 		if ( seed ) {
2249 			if ( postFinder || preFilter ) {
2250 				if ( postFinder ) {
2251 					// Get the final matcherOut by condensing this intermediate into postFinder contexts
2252 					temp = [];
2253 					i = matcherOut.length;
2254 					while ( i-- ) {
2255 						if ( (elem = matcherOut[i]) ) {
2256 							// Restore matcherIn since elem is not yet a final match
2257 							temp.push( (matcherIn[i] = elem) );
2258 						}
2259 					}
2260 					postFinder( null, (matcherOut = []), temp, xml );
2261 				}
2262 
2263 				// Move matched elements from seed to results to keep them synchronized
2264 				i = matcherOut.length;
2265 				while ( i-- ) {
2266 					if ( (elem = matcherOut[i]) &&
2267 						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
2268 
2269 						seed[temp] = !(results[temp] = elem);
2270 					}
2271 				}
2272 			}
2273 
2274 		// Add elements to results, through postFinder if defined
2275 		} else {
2276 			matcherOut = condense(
2277 				matcherOut === results ?
2278 					matcherOut.splice( preexisting, matcherOut.length ) :
2279 					matcherOut
2280 			);
2281 			if ( postFinder ) {
2282 				postFinder( null, results, matcherOut, xml );
2283 			} else {
2284 				push.apply( results, matcherOut );
2285 			}
2286 		}
2287 	});
2288 }
2289 
2290 function matcherFromTokens( tokens ) {
2291 	var checkContext, matcher, j,
2292 		len = tokens.length,
2293 		leadingRelative = Expr.relative[ tokens[0].type ],
2294 		implicitRelative = leadingRelative || Expr.relative[" "],
2295 		i = leadingRelative ? 1 : 0,
2296 
2297 		// The foundational matcher ensures that elements are reachable from top-level context(s)
2298 		matchContext = addCombinator( function( elem ) {
2299 			return elem === checkContext;
2300 		}, implicitRelative, true ),
2301 		matchAnyContext = addCombinator( function( elem ) {
2302 			return indexOf.call( checkContext, elem ) > -1;
2303 		}, implicitRelative, true ),
2304 		matchers = [ function( elem, context, xml ) {
2305 			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
2306 				(checkContext = context).nodeType ?
2307 					matchContext( elem, context, xml ) :
2308 					matchAnyContext( elem, context, xml ) );
2309 		} ];
2310 
2311 	for ( ; i < len; i++ ) {
2312 		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
2313 			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
2314 		} else {
2315 			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
2316 
2317 			// Return special upon seeing a positional matcher
2318 			if ( matcher[ expando ] ) {
2319 				// Find the next relative operator (if any) for proper handling
2320 				j = ++i;
2321 				for ( ; j < len; j++ ) {
2322 					if ( Expr.relative[ tokens[j].type ] ) {
2323 						break;
2324 					}
2325 				}
2326 				return setMatcher(
2327 					i > 1 && elementMatcher( matchers ),
2328 					i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ),
2329 					matcher,
2330 					i < j && matcherFromTokens( tokens.slice( i, j ) ),
2331 					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
2332 					j < len && toSelector( tokens )
2333 				);
2334 			}
2335 			matchers.push( matcher );
2336 		}
2337 	}
2338 
2339 	return elementMatcher( matchers );
2340 }
2341 
2342 function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
2343 	// A counter to specify which element is currently being matched
2344 	var matcherCachedRuns = 0,
2345 		bySet = setMatchers.length > 0,
2346 		byElement = elementMatchers.length > 0,
2347 		superMatcher = function( seed, context, xml, results, expandContext ) {
2348 			var elem, j, matcher,
2349 				setMatched = [],
2350 				matchedCount = 0,
2351 				i = "0",
2352 				unmatched = seed && [],
2353 				outermost = expandContext != null,
2354 				contextBackup = outermostContext,
2355 				// We must always have either seed elements or context
2356 				elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
2357 				// Use integer dirruns iff this is the outermost matcher
2358 				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
2359 
2360 			if ( outermost ) {
2361 				outermostContext = context !== document && context;
2362 				cachedruns = matcherCachedRuns;
2363 			}
2364 
2365 			// Add elements passing elementMatchers directly to results
2366 			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
2367 			for ( ; (elem = elems[i]) != null; i++ ) {
2368 				if ( byElement && elem ) {
2369 					j = 0;
2370 					while ( (matcher = elementMatchers[j++]) ) {
2371 						if ( matcher( elem, context, xml ) ) {
2372 							results.push( elem );
2373 							break;
2374 						}
2375 					}
2376 					if ( outermost ) {
2377 						dirruns = dirrunsUnique;
2378 						cachedruns = ++matcherCachedRuns;
2379 					}
2380 				}
2381 
2382 				// Track unmatched elements for set filters
2383 				if ( bySet ) {
2384 					// They will have gone through all possible matchers
2385 					if ( (elem = !matcher && elem) ) {
2386 						matchedCount--;
2387 					}
2388 
2389 					// Lengthen the array for every element, matched or not
2390 					if ( seed ) {
2391 						unmatched.push( elem );
2392 					}
2393 				}
2394 			}
2395 
2396 			// Apply set filters to unmatched elements
2397 			matchedCount += i;
2398 			if ( bySet && i !== matchedCount ) {
2399 				j = 0;
2400 				while ( (matcher = setMatchers[j++]) ) {
2401 					matcher( unmatched, setMatched, context, xml );
2402 				}
2403 
2404 				if ( seed ) {
2405 					// Reintegrate element matches to eliminate the need for sorting
2406 					if ( matchedCount > 0 ) {
2407 						while ( i-- ) {
2408 							if ( !(unmatched[i] || setMatched[i]) ) {
2409 								setMatched[i] = pop.call( results );
2410 							}
2411 						}
2412 					}
2413 
2414 					// Discard index placeholder values to get only actual matches
2415 					setMatched = condense( setMatched );
2416 				}
2417 
2418 				// Add matches to results
2419 				push.apply( results, setMatched );
2420 
2421 				// Seedless set matches succeeding multiple successful matchers stipulate sorting
2422 				if ( outermost && !seed && setMatched.length > 0 &&
2423 					( matchedCount + setMatchers.length ) > 1 ) {
2424 
2425 					Sizzle.uniqueSort( results );
2426 				}
2427 			}
2428 
2429 			// Override manipulation of globals by nested matchers
2430 			if ( outermost ) {
2431 				dirruns = dirrunsUnique;
2432 				outermostContext = contextBackup;
2433 			}
2434 
2435 			return unmatched;
2436 		};
2437 
2438 	return bySet ?
2439 		markFunction( superMatcher ) :
2440 		superMatcher;
2441 }
2442 
2443 compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
2444 	var i,
2445 		setMatchers = [],
2446 		elementMatchers = [],
2447 		cached = compilerCache[ selector + " " ];
2448 
2449 	if ( !cached ) {
2450 		// Generate a function of recursive functions that can be used to check each element
2451 		if ( !group ) {
2452 			group = tokenize( selector );
2453 		}
2454 		i = group.length;
2455 		while ( i-- ) {
2456 			cached = matcherFromTokens( group[i] );
2457 			if ( cached[ expando ] ) {
2458 				setMatchers.push( cached );
2459 			} else {
2460 				elementMatchers.push( cached );
2461 			}
2462 		}
2463 
2464 		// Cache the compiled function
2465 		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
2466 	}
2467 	return cached;
2468 };
2469 
2470 function multipleContexts( selector, contexts, results ) {
2471 	var i = 0,
2472 		len = contexts.length;
2473 	for ( ; i < len; i++ ) {
2474 		Sizzle( selector, contexts[i], results );
2475 	}
2476 	return results;
2477 }
2478 
2479 function select( selector, context, results, seed ) {
2480 	var i, tokens, token, type, find,
2481 		match = tokenize( selector );
2482 
2483 	if ( !seed ) {
2484 		// Try to minimize operations if there is only one group
2485 		if ( match.length === 1 ) {
2486 
2487 			// Take a shortcut and set the context if the root selector is an ID
2488 			tokens = match[0] = match[0].slice( 0 );
2489 			if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
2490 					context.nodeType === 9 && documentIsHTML &&
2491 					Expr.relative[ tokens[1].type ] ) {
2492 
2493 				context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
2494 				if ( !context ) {
2495 					return results;
2496 				}
2497 
2498 				selector = selector.slice( tokens.shift().value.length );
2499 			}
2500 
2501 			// Fetch a seed set for right-to-left matching
2502 			i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
2503 			while ( i-- ) {
2504 				token = tokens[i];
2505 
2506 				// Abort if we hit a combinator
2507 				if ( Expr.relative[ (type = token.type) ] ) {
2508 					break;
2509 				}
2510 				if ( (find = Expr.find[ type ]) ) {
2511 					// Search, expanding context for leading sibling combinators
2512 					if ( (seed = find(
2513 						token.matches[0].replace( runescape, funescape ),
2514 						rsibling.test( tokens[0].type ) && context.parentNode || context
2515 					)) ) {
2516 
2517 						// If seed is empty or no tokens remain, we can return early
2518 						tokens.splice( i, 1 );
2519 						selector = seed.length && toSelector( tokens );
2520 						if ( !selector ) {
2521 							push.apply( results, seed );
2522 							return results;
2523 						}
2524 
2525 						break;
2526 					}
2527 				}
2528 			}
2529 		}
2530 	}
2531 
2532 	// Compile and execute a filtering function
2533 	// Provide `match` to avoid retokenization if we modified the selector above
2534 	compile( selector, match )(
2535 		seed,
2536 		context,
2537 		!documentIsHTML,
2538 		results,
2539 		rsibling.test( selector )
2540 	);
2541 	return results;
2542 }
2543 
2544 // Deprecated
2545 Expr.pseudos["nth"] = Expr.pseudos["eq"];
2546 
2547 // Easy API for creating new setFilters
2548 function setFilters() {}
2549 setFilters.prototype = Expr.filters = Expr.pseudos;
2550 Expr.setFilters = new setFilters();
2551 
2552 // Check sort stability
2553 support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
2554 
2555 // Initialize with the default document
2556 setDocument();
2557 
2558 // Always assume the presence of duplicates if sort doesn't
2559 // pass them to our comparison function (as in Google Chrome).
2560 [0, 0].sort( sortOrder );
2561 support.detectDuplicates = hasDuplicate;
2562 
2563 /*
2564 // EXPOSE
2565 if ( typeof define === "function" && define.amd ) {
2566 	define(function() { return Sizzle; });
2567 } else {
2568 	window.Sizzle = Sizzle;
2569 }
2570 */
2571 
2572 // EXPOSE
2573 return Sizzle;
2574 });
2575 
2576 // Included from: js/tinymce/classes/dom/DomQuery.js
2577 
2578 /**
2579  * DomQuery.js
2580  *
2581  * Copyright, Moxiecode Systems AB
2582  * Released under LGPL License.
2583  *
2584  * License: http://www.tinymce.com/license
2585  * Contributing: http://www.tinymce.com/contributing
2586  *
2587  * Some of this logic is based on jQuery code that is released under
2588  * MIT license that grants us to sublicense it under LGPL.
2589  *
2590  * @ignore-file
2591  */
2592 
2593 /**
2594  * @class tinymce.dom.DomQuery
2595  */
2596 define("tinymce/dom/DomQuery", [
2597 	"tinymce/dom/EventUtils",
2598 	"tinymce/dom/Sizzle"
2599 ], function(EventUtils, Sizzle) {
2600 	var doc = document, push = Array.prototype.push, slice = Array.prototype.slice;
2601 	var rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
2602 	var Event = EventUtils.Event;
2603 
2604 	function isDefined(obj) {
2605 		return typeof obj !== "undefined";
2606 	}
2607 
2608 	function isString(obj) {
2609 		return typeof obj === "string";
2610 	}
2611 
2612 	function createFragment(html) {
2613 		var frag, node, container;
2614 
2615 		container = doc.createElement("div");
2616 		frag = doc.createDocumentFragment();
2617 		container.innerHTML = html;
2618 
2619 		while ((node = container.firstChild)) {
2620 			frag.appendChild(node);
2621 		}
2622 
2623 		return frag;
2624 	}
2625 
2626 	function domManipulate(targetNodes, sourceItem, callback) {
2627 		var i;
2628 
2629 		if (typeof sourceItem === "string") {
2630 			sourceItem = createFragment(sourceItem);
2631 		} else if (sourceItem.length) {
2632 			for (i = 0; i < sourceItem.length; i++) {
2633 				domManipulate(targetNodes, sourceItem[i], callback);
2634 			}
2635 
2636 			return targetNodes;
2637 		}
2638 
2639 		i = targetNodes.length;
2640 		while (i--) {
2641 			callback.call(targetNodes[i], sourceItem.parentNode ? sourceItem : sourceItem);
2642 		}
2643 
2644 		return targetNodes;
2645 	}
2646 
2647 	function hasClass(node, className) {
2648 		return node && className && (' ' + node.className + ' ').indexOf(' ' + className + ' ') !== -1;
2649 	}
2650 
2651 	/**
2652 	 * Makes a map object out of a string that gets separated by a delimiter.
2653 	 *
2654 	 * @method makeMap
2655 	 * @param {String} items Item string to split.
2656 	 * @param {Object} map Optional object to add items to.
2657 	 * @return {Object} name/value object with items as keys.
2658 	 */
2659 	function makeMap(items, map) {
2660 		var i;
2661 
2662 		items = items || [];
2663 
2664 		if (typeof(items) == "string") {
2665 			items = items.split(' ');
2666 		}
2667 
2668 		map = map || {};
2669 
2670 		i = items.length;
2671 		while (i--) {
2672 			map[items[i]] = {};
2673 		}
2674 
2675 		return map;
2676 	}
2677 
2678 	var numericCssMap = makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom');
2679 
2680 	function DomQuery(selector, context) {
2681 		/*eslint new-cap:0 */
2682 		return new DomQuery.fn.init(selector, context);
2683 	}
2684 
2685 	/**
2686 	 * Extends the specified object with another object.
2687 	 *
2688 	 * @method extend
2689 	 * @param {Object} target Object to extend.
2690 	 * @param {Object..} obj Multiple objects to extend with.
2691 	 * @return {Object} Same as target, the extended object.
2692 	 */
2693 	function extend(target) {
2694 		var args = arguments, arg, i, key;
2695 
2696 		for (i = 1; i < args.length; i++) {
2697 			arg = args[i];
2698 
2699 			for (key in arg) {
2700 				target[key] = arg[key];
2701 			}
2702 		}
2703 
2704 		return target;
2705 	}
2706 
2707 	/**
2708 	 * Converts the specified object into a real JavaScript array.
2709 	 *
2710 	 * @method toArray
2711 	 * @param {Object} obj Object to convert into array.
2712 	 * @return {Array} Array object based in input.
2713 	 */
2714 	function toArray(obj) {
2715 		var array = [], i, l;
2716 
2717 		for (i = 0, l = obj.length; i < l; i++) {
2718 			array[i] = obj[i];
2719 		}
2720 
2721 		return array;
2722 	}
2723 
2724 	/**
2725 	 * Returns the index of the specified item inside the array.
2726 	 *
2727 	 * @method inArray
2728 	 * @param {Object} item Item to look for.
2729 	 * @param {Array} array Array to look for item in.
2730 	 * @return {Number} Index of the item or -1.
2731 	 */
2732 	function inArray(item, array) {
2733 		var i;
2734 
2735 		if (array.indexOf) {
2736 			return array.indexOf(item);
2737 		}
2738 
2739 		i = array.length;
2740 		while (i--) {
2741 			if (array[i] === item) {
2742 				return i;
2743 			}
2744 		}
2745 
2746 		return -1;
2747 	}
2748 
2749 	/**
2750 	 * Returns true/false if the specified object is an array.
2751 	 *
2752 	 * @method isArray
2753 	 * @param {Object} obj Object to check if it's an array.
2754 	 * @return {Boolean} true/false if the input object is array or not.
2755 	 */
2756 	var isArray = Array.isArray || function(obj) {
2757 		return Object.prototype.toString.call(obj) === "[object Array]";
2758 	};
2759 
2760 	var whiteSpaceRegExp = /^\s*|\s*$/g;
2761 
2762 	function trim(str) {
2763 		return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
2764 	}
2765 
2766 	/**
2767 	 * Executes the callback function for each item in array/object. If you return false in the
2768 	 * callback it will break the loop.
2769 	 *
2770 	 * @method each
2771 	 * @param {Object} obj Object to iterate.
2772 	 * @param {function} callback Callback function to execute for each item.
2773 	 */
2774 	function each(obj, callback) {
2775 		var length, key, i, undef, value;
2776 
2777 		if (obj) {
2778 			length = obj.length;
2779 
2780 			if (length === undef) {
2781 				// Loop object items
2782 				for (key in obj) {
2783 					if (obj.hasOwnProperty(key)) {
2784 						value = obj[key];
2785 						if (callback.call(value, value, key) === false) {
2786 							break;
2787 						}
2788 					}
2789 				}
2790 			} else {
2791 				// Loop array items
2792 				for (i = 0; i < length; i++) {
2793 					value = obj[i];
2794 					if (callback.call(value, value, key) === false) {
2795 						break;
2796 					}
2797 				}
2798 			}
2799 		}
2800 
2801 		return obj;
2802 	}
2803 
2804 	DomQuery.fn = DomQuery.prototype = {
2805 		constructor: DomQuery,
2806 		selector: "",
2807 		length: 0,
2808 
2809 		init: function(selector, context) {
2810 			var self = this, match, node;
2811 
2812 			if (!selector) {
2813 				return self;
2814 			}
2815 
2816 			if (selector.nodeType) {
2817 				self.context = self[0] = selector;
2818 				self.length = 1;
2819 
2820 				return self;
2821 			}
2822 
2823 			if (isString(selector)) {
2824 				if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
2825 					match = [null, selector, null];
2826 				} else {
2827 					match = rquickExpr.exec(selector);
2828 				}
2829 
2830 				if (match) {
2831 					if (match[1]) {
2832 						node = createFragment(selector).firstChild;
2833 						while (node) {
2834 							this.add(node);
2835 							node = node.nextSibling;
2836 						}
2837 					} else {
2838 						node = doc.getElementById(match[2]);
2839 
2840 						if (node.id !== match[2]) {
2841 							return self.find(selector);
2842 						}
2843 
2844 						self.length = 1;
2845 						self[0] = node;
2846 					}
2847 				} else {
2848 					return DomQuery(context || document).find(selector);
2849 				}
2850 			} else {
2851 				this.add(selector);
2852 			}
2853 
2854 			return self;
2855 		},
2856 
2857 		toArray: function() {
2858 			return toArray(this);
2859 		},
2860 
2861 		add: function(items) {
2862 			var self = this;
2863 
2864 			// Force single item into array
2865 			if (!isArray(items)) {
2866 				if (items instanceof DomQuery) {
2867 					self.add(items.toArray());
2868 				} else {
2869 					push.call(self, items);
2870 				}
2871 			} else {
2872 				push.apply(self, items);
2873 			}
2874 
2875 			return self;
2876 		},
2877 
2878 		attr: function(name, value) {
2879 			var self = this;
2880 
2881 			if (typeof name === "object") {
2882 				each(name, function(value, name) {
2883 					self.attr(name, value);
2884 				});
2885 			} else if (isDefined(value)) {
2886 				this.each(function() {
2887 					if (this.nodeType === 1) {
2888 						this.setAttribute(name, value);
2889 					}
2890 				});
2891 			} else {
2892 				return self[0] && self[0].nodeType === 1 ? self[0].getAttribute(name) : undefined;
2893 			}
2894 
2895 			return self;
2896 		},
2897 
2898 		css: function(name, value) {
2899 			var self = this;
2900 
2901 			if (typeof name === "object") {
2902 				each(name, function(value, name) {
2903 					self.css(name, value);
2904 				});
2905 			} else {
2906 				// Camelcase it, if needed
2907 				name = name.replace(/-(\D)/g, function(a, b) {
2908 					return b.toUpperCase();
2909 				});
2910 
2911 				if (isDefined(value)) {
2912 					// Default px suffix on these
2913 					if (typeof(value) === 'number' && !numericCssMap[name]) {
2914 						value += 'px';
2915 					}
2916 
2917 					self.each(function() {
2918 						var style = this.style;
2919 
2920 						// IE specific opacity
2921 						if (name === "opacity" && this.runtimeStyle && typeof(this.runtimeStyle.opacity) === "undefined") {
2922 							style.filter = value === '' ? '' : "alpha(opacity=" + (value * 100) + ")";
2923 						}
2924 
2925 						try {
2926 							style[name] = value;
2927 						} catch (ex) {
2928 							// Ignore
2929 						}
2930 					});
2931 				} else {
2932 					return self[0] ? self[0].style[name] : undefined;
2933 				}
2934 			}
2935 
2936 			return self;
2937 		},
2938 
2939 		remove: function() {
2940 			var self = this, node, i = this.length;
2941 
2942 			while (i--) {
2943 				node = self[i];
2944 				Event.clean(node);
2945 
2946 				if (node.parentNode) {
2947 					node.parentNode.removeChild(node);
2948 				}
2949 			}
2950 
2951 			return this;
2952 		},
2953 
2954 		empty: function() {
2955 			var self = this, node, i = this.length;
2956 
2957 			while (i--) {
2958 				node = self[i];
2959 				while (node.firstChild) {
2960 					node.removeChild(node.firstChild);
2961 				}
2962 			}
2963 
2964 			return this;
2965 		},
2966 
2967 		html: function(value) {
2968 			var self = this, i;
2969 
2970 			if (isDefined(value)) {
2971 				i = self.length;
2972 				while (i--) {
2973 					self[i].innerHTML = value;
2974 				}
2975 
2976 				return self;
2977 			}
2978 
2979 			return self[0] ? self[0].innerHTML : '';
2980 		},
2981 
2982 		text: function(value) {
2983 			var self = this, i;
2984 
2985 			if (isDefined(value)) {
2986 				i = self.length;
2987 				while (i--) {
2988 					self[i].innerText = self[0].textContent = value;
2989 				}
2990 
2991 				return self;
2992 			}
2993 
2994 			return self[0] ? self[0].innerText || self[0].textContent : '';
2995 		},
2996 
2997 		append: function() {
2998 			return domManipulate(this, arguments, function(node) {
2999 				if (this.nodeType === 1) {
3000 					this.appendChild(node);
3001 				}
3002 			});
3003 		},
3004 
3005 		prepend: function() {
3006 			return domManipulate(this, arguments, function(node) {
3007 				if (this.nodeType === 1) {
3008 					this.insertBefore(node, this.firstChild);
3009 				}
3010 			});
3011 		},
3012 
3013 		before: function() {
3014 			var self = this;
3015 
3016 			if (self[0] && self[0].parentNode) {
3017 				return domManipulate(self, arguments, function(node) {
3018 					this.parentNode.insertBefore(node, this.nextSibling);
3019 				});
3020 			}
3021 
3022 			return self;
3023 		},
3024 
3025 		after: function() {
3026 			var self = this;
3027 
3028 			if (self[0] && self[0].parentNode) {
3029 				return domManipulate(self, arguments, function(node) {
3030 					this.parentNode.insertBefore(node, this);
3031 				});
3032 			}
3033 
3034 			return self;
3035 		},
3036 
3037 		appendTo: function(val) {
3038 			DomQuery(val).append(this);
3039 
3040 			return this;
3041 		},
3042 
3043 		addClass: function(className) {
3044 			return this.toggleClass(className, true);
3045 		},
3046 
3047 		removeClass: function(className) {
3048 			return this.toggleClass(className, false);
3049 		},
3050 
3051 		toggleClass: function(className, state) {
3052 			var self = this;
3053 
3054 			if (className.indexOf(' ') !== -1) {
3055 				each(className.split(' '), function() {
3056 					self.toggleClass(this, state);
3057 				});
3058 			} else {
3059 				self.each(function(node) {
3060 					var existingClassName;
3061 
3062 					if (hasClass(node, className) !== state) {
3063 						existingClassName = node.className;
3064 
3065 						if (state) {
3066 							node.className += existingClassName ? ' ' + className : className;
3067 						} else {
3068 							node.className = trim((" " + existingClassName + " ").replace(' ' + className + ' ', ' '));
3069 						}
3070 					}
3071 				});
3072 			}
3073 
3074 			return self;
3075 		},
3076 
3077 		hasClass: function(className) {
3078 			return hasClass(this[0], className);
3079 		},
3080 
3081 		each: function(callback) {
3082 			return each(this, callback);
3083 		},
3084 
3085 		on: function(name, callback) {
3086 			return this.each(function() {
3087 				Event.bind(this, name, callback);
3088 			});
3089 		},
3090 
3091 		off: function(name, callback) {
3092 			return this.each(function() {
3093 				Event.unbind(this, name, callback);
3094 			});
3095 		},
3096 
3097 		show: function() {
3098 			return this.css('display', '');
3099 		},
3100 
3101 		hide: function() {
3102 			return this.css('display', 'none');
3103 		},
3104 
3105 		slice: function() {
3106 			return new DomQuery(slice.apply(this, arguments));
3107 		},
3108 
3109 		eq: function(index) {
3110 			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
3111 		},
3112 
3113 		first: function() {
3114 			return this.eq(0);
3115 		},
3116 
3117 		last: function() {
3118 			return this.eq(-1);
3119 		},
3120 
3121 		replaceWith: function(content) {
3122 			var self = this;
3123 
3124 			if (self[0]) {
3125 				self[0].parentNode.replaceChild(DomQuery(content)[0], self[0]);
3126 			}
3127 
3128 			return self;
3129 		},
3130 
3131 		wrap: function(wrapper) {
3132 			wrapper = DomQuery(wrapper)[0];
3133 
3134 			return this.each(function() {
3135 				var self = this, newWrapper = wrapper.cloneNode(false);
3136 				self.parentNode.insertBefore(newWrapper, self);
3137 				newWrapper.appendChild(self);
3138 			});
3139 		},
3140 
3141 		unwrap: function() {
3142 			return this.each(function() {
3143 				var self = this, node = self.firstChild, currentNode;
3144 
3145 				while (node) {
3146 					currentNode = node;
3147 					node = node.nextSibling;
3148 					self.parentNode.insertBefore(currentNode, self);
3149 				}
3150 			});
3151 		},
3152 
3153 		clone: function() {
3154 			var result = [];
3155 
3156 			this.each(function() {
3157 				result.push(this.cloneNode(true));
3158 			});
3159 
3160 			return DomQuery(result);
3161 		},
3162 
3163 		find: function(selector) {
3164 			var i, l, ret = [];
3165 
3166 			for (i = 0, l = this.length; i < l; i++) {
3167 				DomQuery.find(selector, this[i], ret);
3168 			}
3169 
3170 			return DomQuery(ret);
3171 		},
3172 
3173 		push: push,
3174 		sort: [].sort,
3175 		splice: [].splice
3176 	};
3177 
3178 	// Static members
3179 	extend(DomQuery, {
3180 		extend: extend,
3181 		toArray: toArray,
3182 		inArray: inArray,
3183 		isArray: isArray,
3184 		each: each,
3185 		trim: trim,
3186 		makeMap: makeMap,
3187 
3188 		// Sizzle
3189 		find: Sizzle,
3190 		expr: Sizzle.selectors,
3191 		unique: Sizzle.uniqueSort,
3192 		text: Sizzle.getText,
3193 		isXMLDoc: Sizzle.isXML,
3194 		contains: Sizzle.contains,
3195 		filter: function(expr, elems, not) {
3196 			if (not) {
3197 				expr = ":not(" + expr + ")";
3198 			}
3199 
3200 			if (elems.length === 1) {
3201 				elems = DomQuery.find.matchesSelector(elems[0], expr) ? [elems[0]] : [];
3202 			} else {
3203 				elems = DomQuery.find.matches(expr, elems);
3204 			}
3205 
3206 			return elems;
3207 		}
3208 	});
3209 
3210 	function dir(el, prop, until) {
3211 		var matched = [], cur = el[prop];
3212 
3213 		while (cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !DomQuery(cur).is(until))) {
3214 			if (cur.nodeType === 1) {
3215 				matched.push(cur);
3216 			}
3217 
3218 			cur = cur[prop];
3219 		}
3220 
3221 		return matched;
3222 	}
3223 
3224 	function sibling(n, el, siblingName, nodeType) {
3225 		var r = [];
3226 
3227 		for(; n; n = n[siblingName]) {
3228 			if ((!nodeType || n.nodeType === nodeType) && n !== el) {
3229 				r.push(n);
3230 			}
3231 		}
3232 
3233 		return r;
3234 	}
3235 
3236 	each({
3237 		parent: function(node) {
3238 			var parent = node.parentNode;
3239 
3240 			return parent && parent.nodeType !== 11 ? parent : null;
3241 		},
3242 
3243 		parents: function(node) {
3244 			return dir(node, "parentNode");
3245 		},
3246 
3247 		parentsUntil: function(node, until) {
3248 			return dir(node, "parentNode", until);
3249 		},
3250 
3251 		next: function(node) {
3252 			return sibling(node, 'nextSibling', 1);
3253 		},
3254 
3255 		prev: function(node) {
3256 			return sibling(node, 'previousSibling', 1);
3257 		},
3258 
3259 		nextNodes: function(node) {
3260 			return sibling(node, 'nextSibling');
3261 		},
3262 
3263 		prevNodes: function(node) {
3264 			return sibling(node, 'previousSibling');
3265 		},
3266 
3267 		children: function(node) {
3268 			return sibling(node.firstChild, 'nextSibling', 1);
3269 		},
3270 
3271 		contents: function(node) {
3272 			return toArray((node.nodeName === "iframe" ? node.contentDocument || node.contentWindow.document : node).childNodes);
3273 		}
3274 	}, function(name, fn){
3275 		DomQuery.fn[name] = function(selector) {
3276 			var self = this, result;
3277 
3278 			if (self.length > 1) {
3279 				throw new Error("DomQuery only supports traverse functions on a single node.");
3280 			}
3281 
3282 			if (self[0]) {
3283 				result = fn(self[0], selector);
3284 			}
3285 
3286 			result = DomQuery(result);
3287 
3288 			if (selector && name !== "parentsUntil") {
3289 				return result.filter(selector);
3290 			}
3291 
3292 			return result;
3293 		};
3294 	});
3295 
3296 	DomQuery.fn.filter = function(selector) {
3297 		return DomQuery.filter(selector);
3298 	};
3299 
3300 	DomQuery.fn.is = function(selector) {
3301 		return !!selector && this.filter(selector).length > 0;
3302 	};
3303 
3304 	DomQuery.fn.init.prototype = DomQuery.fn;
3305 
3306 	return DomQuery;
3307 });
3308 
3309 // Included from: js/tinymce/classes/html/Styles.js
3310 
3311 /**
3312  * Styles.js
3313  *
3314  * Copyright, Moxiecode Systems AB
3315  * Released under LGPL License.
3316  *
3317  * License: http://www.tinymce.com/license
3318  * Contributing: http://www.tinymce.com/contributing
3319  */
3320 
3321 /**
3322  * This class is used to parse CSS styles it also compresses styles to reduce the output size.
3323  *
3324  * @example
3325  * var Styles = new tinymce.html.Styles({
3326  *    url_converter: function(url) {
3327  *       return url;
3328  *    }
3329  * });
3330  *
3331  * styles = Styles.parse('border: 1px solid red');
3332  * styles.color = 'red';
3333  *
3334  * console.log(new tinymce.html.StyleSerializer().serialize(styles));
3335  *
3336  * @class tinymce.html.Styles
3337  * @version 3.4
3338  */
3339 define("tinymce/html/Styles", [], function() {
3340 	return function(settings, schema) {
3341 		/*jshint maxlen:255 */
3342 		/*eslint max-len:0 */
3343 		var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
3344 			urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
3345 			styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
3346 			trimRightRegExp = /\s+$/,
3347 			undef, i, encodingLookup = {}, encodingItems, invisibleChar = '\uFEFF';
3348 
3349 		settings = settings || {};
3350 
3351 		encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' ');
3352 		for (i = 0; i < encodingItems.length; i++) {
3353 			encodingLookup[encodingItems[i]] = invisibleChar + i;
3354 			encodingLookup[invisibleChar + i] = encodingItems[i];
3355 		}
3356 
3357 		function toHex(match, r, g, b) {
3358 			function hex(val) {
3359 				val = parseInt(val, 10).toString(16);
3360 
3361 				return val.length > 1 ? val : '0' + val; // 0 -> 00
3362 			}
3363 
3364 			return '#' + hex(r) + hex(g) + hex(b);
3365 		}
3366 
3367 		return {
3368 			/**
3369 			 * Parses the specified RGB color value and returns a hex version of that color.
3370 			 *
3371 			 * @method toHex
3372 			 * @param {String} color RGB string value like rgb(1,2,3)
3373 			 * @return {String} Hex version of that RGB value like #FF00FF.
3374 			 */
3375 			toHex: function(color) {
3376 				return color.replace(rgbRegExp, toHex);
3377 			},
3378 
3379 			/**
3380 			 * Parses the specified style value into an object collection. This parser will also
3381 			 * merge and remove any redundant items that browsers might have added. It will also convert non hex
3382 			 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
3383 			 *
3384 			 * @method parse
3385 			 * @param {String} css Style value to parse for example: border:1px solid red;.
3386 			 * @return {Object} Object representation of that style like {border: '1px solid red'}
3387 			 */
3388 			parse: function(css) {
3389 				var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter;
3390 				var urlConverterScope = settings.url_converter_scope || this;
3391 
3392 				function compress(prefix, suffix, noJoin) {
3393 					var top, right, bottom, left;
3394 
3395 					top = styles[prefix + '-top' + suffix];
3396 					if (!top) {
3397 						return;
3398 					}
3399 
3400 					right = styles[prefix + '-right' + suffix];
3401 					if (!right) {
3402 						return;
3403 					}
3404 
3405 					bottom = styles[prefix + '-bottom' + suffix];
3406 					if (!bottom) {
3407 						return;
3408 					}
3409 
3410 					left = styles[prefix + '-left' + suffix];
3411 					if (!left) {
3412 						return;
3413 					}
3414 
3415 					var box = [top, right, bottom, left];
3416 					i = box.length - 1;
3417 					while (i--) {
3418 						if (box[i] !== box[i + 1]) {
3419 							break;
3420 						}
3421 					}
3422 
3423 					if (i > -1 && noJoin) {
3424 						return;
3425 					}
3426 
3427 					styles[prefix + suffix] = i == -1 ? box[0] : box.join(' ');
3428 					delete styles[prefix + '-top' + suffix];
3429 					delete styles[prefix + '-right' + suffix];
3430 					delete styles[prefix + '-bottom' + suffix];
3431 					delete styles[prefix + '-left' + suffix];
3432 				}
3433 
3434 				/**
3435 				 * Checks if the specific style can be compressed in other words if all border-width are equal.
3436 				 */
3437 				function canCompress(key) {
3438 					var value = styles[key], i;
3439 
3440 					if (!value) {
3441 						return;
3442 					}
3443 
3444 					value = value.split(' ');
3445 					i = value.length;
3446 					while (i--) {
3447 						if (value[i] !== value[0]) {
3448 							return false;
3449 						}
3450 					}
3451 
3452 					styles[key] = value[0];
3453 
3454 					return true;
3455 				}
3456 
3457 				/**
3458 				 * Compresses multiple styles into one style.
3459 				 */
3460 				function compress2(target, a, b, c) {
3461 					if (!canCompress(a)) {
3462 						return;
3463 					}
3464 
3465 					if (!canCompress(b)) {
3466 						return;
3467 					}
3468 
3469 					if (!canCompress(c)) {
3470 						return;
3471 					}
3472 
3473 					// Compress
3474 					styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
3475 					delete styles[a];
3476 					delete styles[b];
3477 					delete styles[c];
3478 				}
3479 
3480 				// Encodes the specified string by replacing all \" \' ; : with _<num>
3481 				function encode(str) {
3482 					isEncoded = true;
3483 
3484 					return encodingLookup[str];
3485 				}
3486 
3487 				// Decodes the specified string by replacing all _<num> with it's original value \" \' etc
3488 				// It will also decode the \" \' if keep_slashes is set to fale or omitted
3489 				function decode(str, keep_slashes) {
3490 					if (isEncoded) {
3491 						str = str.replace(/\uFEFF[0-9]/g, function(str) {
3492 							return encodingLookup[str];
3493 						});
3494 					}
3495 
3496 					if (!keep_slashes) {
3497 						str = str.replace(/\\([\'\";:])/g, "$1");
3498 					}
3499 
3500 					return str;
3501 				}
3502 
3503 				function processUrl(match, url, url2, url3, str, str2) {
3504 					str = str || str2;
3505 
3506 					if (str) {
3507 						str = decode(str);
3508 
3509 						// Force strings into single quote format
3510 						return "'" + str.replace(/\'/g, "\\'") + "'";
3511 					}
3512 
3513 					url = decode(url || url2 || url3);
3514 
3515 					if (!settings.allow_script_urls) {
3516 						var scriptUrl = url.replace(/[\s\r\n]+/, '');
3517 
3518 						if (/(java|vb)script:/i.test(scriptUrl)) {
3519 							return "";
3520 						}
3521 
3522 						if (!settings.allow_svg_data_urls && /^data:image\/svg/i.test(scriptUrl)) {
3523 							return "";
3524 						}
3525 					}
3526 
3527 					// Convert the URL to relative/absolute depending on config
3528 					if (urlConverter) {
3529 						url = urlConverter.call(urlConverterScope, url, 'style');
3530 					}
3531 
3532 					// Output new URL format
3533 					return "url('" + url.replace(/\'/g, "\\'") + "')";
3534 				}
3535 
3536 				if (css) {
3537 					css = css.replace(/[\u0000-\u001F]/g, '');
3538 
3539 					// Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
3540 					css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
3541 						return str.replace(/[;:]/g, encode);
3542 					});
3543 
3544 					// Parse styles
3545 					while ((matches = styleRegExp.exec(css))) {
3546 						name = matches[1].replace(trimRightRegExp, '').toLowerCase();
3547 						value = matches[2].replace(trimRightRegExp, '');
3548 
3549 						// Decode escaped sequences like \65 -> e
3550 						/*jshint loopfunc:true*/
3551 						/*eslint no-loop-func:0 */
3552 						value = value.replace(/\\[0-9a-f]+/g, function(e) {
3553 							return String.fromCharCode(parseInt(e.substr(1), 16));
3554 						});
3555 
3556 						if (name && value.length > 0) {
3557 							// Don't allow behavior name or expression/comments within the values
3558 							if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(|\/\*|\*\//.test(value))) {
3559 								continue;
3560 							}
3561 
3562 							// Opera will produce 700 instead of bold in their style values
3563 							if (name === 'font-weight' && value === '700') {
3564 								value = 'bold';
3565 							} else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED
3566 								value = value.toLowerCase();
3567 							}
3568 
3569 							// Convert RGB colors to HEX
3570 							value = value.replace(rgbRegExp, toHex);
3571 
3572 							// Convert URLs and force them into url('value') format
3573 							value = value.replace(urlOrStrRegExp, processUrl);
3574 							styles[name] = isEncoded ? decode(value, true) : value;
3575 						}
3576 
3577 						styleRegExp.lastIndex = matches.index + matches[0].length;
3578 					}
3579 					// Compress the styles to reduce it's size for example IE will expand styles
3580 					compress("border", "", true);
3581 					compress("border", "-width");
3582 					compress("border", "-color");
3583 					compress("border", "-style");
3584 					compress("padding", "");
3585 					compress("margin", "");
3586 					compress2('border', 'border-width', 'border-style', 'border-color');
3587 
3588 					// Remove pointless border, IE produces these
3589 					if (styles.border === 'medium none') {
3590 						delete styles.border;
3591 					}
3592 
3593 					// IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>
3594 					// So lets asume it shouldn't be there
3595 					if (styles['border-image'] === 'none') {
3596 						delete styles['border-image'];
3597 					}
3598 				}
3599 
3600 				return styles;
3601 			},
3602 
3603 			/**
3604 			 * Serializes the specified style object into a string.
3605 			 *
3606 			 * @method serialize
3607 			 * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'}
3608 			 * @param {String} element_name Optional element name, if specified only the styles that matches the schema will be serialized.
3609 			 * @return {String} String representation of the style object for example: border: 1px solid red.
3610 			 */
3611 			serialize: function(styles, element_name) {
3612 				var css = '', name, value;
3613 
3614 				function serializeStyles(name) {
3615 					var styleList, i, l, value;
3616 
3617 					styleList = schema.styles[name];
3618 					if (styleList) {
3619 						for (i = 0, l = styleList.length; i < l; i++) {
3620 							name = styleList[i];
3621 							value = styles[name];
3622 
3623 							if (value !== undef && value.length > 0) {
3624 								css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
3625 							}
3626 						}
3627 					}
3628 				}
3629 
3630 				// Serialize styles according to schema
3631 				if (element_name && schema && schema.styles) {
3632 					// Serialize global styles and element specific styles
3633 					serializeStyles('*');
3634 					serializeStyles(element_name);
3635 				} else {
3636 					// Output the styles in the order they are inside the object
3637 					for (name in styles) {
3638 						value = styles[name];
3639 
3640 						if (value !== undef && value.length > 0) {
3641 							css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
3642 						}
3643 					}
3644 				}
3645 
3646 				return css;
3647 			}
3648 		};
3649 	};
3650 });
3651 
3652 // Included from: js/tinymce/classes/dom/TreeWalker.js
3653 
3654 /**
3655  * TreeWalker.js
3656  *
3657  * Copyright, Moxiecode Systems AB
3658  * Released under LGPL License.
3659  *
3660  * License: http://www.tinymce.com/license
3661  * Contributing: http://www.tinymce.com/contributing
3662  */
3663 
3664 /**
3665  * TreeWalker class enables you to walk the DOM in a linear manner.
3666  *
3667  * @class tinymce.dom.TreeWalker
3668  */
3669 define("tinymce/dom/TreeWalker", [], function() {
3670 	return function(start_node, root_node) {
3671 		var node = start_node;
3672 
3673 		function findSibling(node, start_name, sibling_name, shallow) {
3674 			var sibling, parent;
3675 
3676 			if (node) {
3677 				// Walk into nodes if it has a start
3678 				if (!shallow && node[start_name]) {
3679 					return node[start_name];
3680 				}
3681 
3682 				// Return the sibling if it has one
3683 				if (node != root_node) {
3684 					sibling = node[sibling_name];
3685 					if (sibling) {
3686 						return sibling;
3687 					}
3688 
3689 					// Walk up the parents to look for siblings
3690 					for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {
3691 						sibling = parent[sibling_name];
3692 						if (sibling) {
3693 							return sibling;
3694 						}
3695 					}
3696 				}
3697 			}
3698 		}
3699 
3700 		/**
3701 		 * Returns the current node.
3702 		 *
3703 		 * @method current
3704 		 * @return {Node} Current node where the walker is.
3705 		 */
3706 		this.current = function() {
3707 			return node;
3708 		};
3709 
3710 		/**
3711 		 * Walks to the next node in tree.
3712 		 *
3713 		 * @method next
3714 		 * @return {Node} Current node where the walker is after moving to the next node.
3715 		 */
3716 		this.next = function(shallow) {
3717 			node = findSibling(node, 'firstChild', 'nextSibling', shallow);
3718 			return node;
3719 		};
3720 
3721 		/**
3722 		 * Walks to the previous node in tree.
3723 		 *
3724 		 * @method prev
3725 		 * @return {Node} Current node where the walker is after moving to the previous node.
3726 		 */
3727 		this.prev = function(shallow) {
3728 			node = findSibling(node, 'lastChild', 'previousSibling', shallow);
3729 			return node;
3730 		};
3731 	};
3732 });
3733 
3734 // Included from: js/tinymce/classes/util/Tools.js
3735 
3736 /**
3737  * Tools.js
3738  *
3739  * Copyright, Moxiecode Systems AB
3740  * Released under LGPL License.
3741  *
3742  * License: http://www.tinymce.com/license
3743  * Contributing: http://www.tinymce.com/contributing
3744  */
3745 
3746 /**
3747  * This class contains various utlity functions. These are also exposed
3748  * directly on the tinymce namespace.
3749  *
3750  * @class tinymce.util.Tools
3751  */
3752 define("tinymce/util/Tools", [], function() {
3753 	/**
3754 	 * Removes whitespace from the beginning and end of a string.
3755 	 *
3756 	 * @method trim
3757 	 * @param {String} s String to remove whitespace from.
3758 	 * @return {String} New string with removed whitespace.
3759 	 */
3760 	var whiteSpaceRegExp = /^\s*|\s*$/g;
3761 
3762 	function trim(str) {
3763 		return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
3764 	}
3765 
3766 	/**
3767 	 * Returns true/false if the object is an array or not.
3768 	 *
3769 	 * @method isArray
3770 	 * @param {Object} obj Object to check.
3771 	 * @return {boolean} true/false state if the object is an array or not.
3772 	 */
3773 	var isArray = Array.isArray || function(obj) {
3774 		return Object.prototype.toString.call(obj) === "[object Array]";
3775 	};
3776 
3777 	/**
3778 	 * Checks if a object is of a specific type for example an array.
3779 	 *
3780 	 * @method is
3781 	 * @param {Object} o Object to check type of.
3782 	 * @param {string} t Optional type to check for.
3783 	 * @return {Boolean} true/false if the object is of the specified type.
3784 	 */
3785 	function is(o, t) {
3786 		if (!t) {
3787 			return o !== undefined;
3788 		}
3789 
3790 		if (t == 'array' && isArray(o)) {
3791 			return true;
3792 		}
3793 
3794 		return typeof(o) == t;
3795 	}
3796 
3797 	/**
3798 	 * Converts the specified object into a real JavaScript array.
3799 	 *
3800 	 * @method toArray
3801 	 * @param {Object} obj Object to convert into array.
3802 	 * @return {Array} Array object based in input.
3803 	 */
3804 	function toArray(obj) {
3805 		var array = [], i, l;
3806 
3807 		for (i = 0, l = obj.length; i < l; i++) {
3808 			array[i] = obj[i];
3809 		}
3810 
3811 		return array;
3812 	}
3813 
3814 	/**
3815 	 * Makes a name/object map out of an array with names.
3816 	 *
3817 	 * @method makeMap
3818 	 * @param {Array/String} items Items to make map out of.
3819 	 * @param {String} delim Optional delimiter to split string by.
3820 	 * @param {Object} map Optional map to add items to.
3821 	 * @return {Object} Name/value map of items.
3822 	 */
3823 	function makeMap(items, delim, map) {
3824 		var i;
3825 
3826 		items = items || [];
3827 		delim = delim || ',';
3828 
3829 		if (typeof(items) == "string") {
3830 			items = items.split(delim);
3831 		}
3832 
3833 		map = map || {};
3834 
3835 		i = items.length;
3836 		while (i--) {
3837 			map[items[i]] = {};
3838 		}
3839 
3840 		return map;
3841 	}
3842 
3843 	/**
3844 	 * Performs an iteration of all items in a collection such as an object or array. This method will execure the
3845 	 * callback function for each item in the collection, if the callback returns false the iteration will terminate.
3846 	 * The callback has the following format: cb(value, key_or_index).
3847 	 *
3848 	 * @method each
3849 	 * @param {Object} o Collection to iterate.
3850 	 * @param {function} cb Callback function to execute for each item.
3851 	 * @param {Object} s Optional scope to execute the callback in.
3852 	 * @example
3853 	 * // Iterate an array
3854 	 * tinymce.each([1,2,3], function(v, i) {
3855 	 *     console.debug("Value: " + v + ", Index: " + i);
3856 	 * });
3857 	 *
3858 	 * // Iterate an object
3859 	 * tinymce.each({a: 1, b: 2, c: 3], function(v, k) {
3860 	 *     console.debug("Value: " + v + ", Key: " + k);
3861 	 * });
3862 	 */
3863 	function each(o, cb, s) {
3864 		var n, l;
3865 
3866 		if (!o) {
3867 			return 0;
3868 		}
3869 
3870 		s = s || o;
3871 
3872 		if (o.length !== undefined) {
3873 			// Indexed arrays, needed for Safari
3874 			for (n = 0, l = o.length; n < l; n++) {
3875 				if (cb.call(s, o[n], n, o) === false) {
3876 					return 0;
3877 				}
3878 			}
3879 		} else {
3880 			// Hashtables
3881 			for (n in o) {
3882 				if (o.hasOwnProperty(n)) {
3883 					if (cb.call(s, o[n], n, o) === false) {
3884 						return 0;
3885 					}
3886 				}
3887 			}
3888 		}
3889 
3890 		return 1;
3891 	}
3892 
3893 	/**
3894 	 * Creates a new array by the return value of each iteration function call. This enables you to convert
3895 	 * one array list into another.
3896 	 *
3897 	 * @method map
3898 	 * @param {Array} a Array of items to iterate.
3899 	 * @param {function} f Function to call for each item. It's return value will be the new value.
3900 	 * @return {Array} Array with new values based on function return values.
3901 	 */
3902 	function map(a, f) {
3903 		var o = [];
3904 
3905 		each(a, function(v) {
3906 			o.push(f(v));
3907 		});
3908 
3909 		return o;
3910 	}
3911 
3912 	/**
3913 	 * Filters out items from the input array by calling the specified function for each item.
3914 	 * If the function returns false the item will be excluded if it returns true it will be included.
3915 	 *
3916 	 * @method grep
3917 	 * @param {Array} a Array of items to loop though.
3918 	 * @param {function} f Function to call for each item. Include/exclude depends on it's return value.
3919 	 * @return {Array} New array with values imported and filtered based in input.
3920 	 * @example
3921 	 * // Filter out some items, this will return an array with 4 and 5
3922 	 * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;});
3923 	 */
3924 	function grep(a, f) {
3925 		var o = [];
3926 
3927 		each(a, function(v) {
3928 			if (!f || f(v)) {
3929 				o.push(v);
3930 			}
3931 		});
3932 
3933 		return o;
3934 	}
3935 
3936 	/**
3937 	 * Creates a class, subclass or static singleton.
3938 	 * More details on this method can be found in the Wiki.
3939 	 *
3940 	 * @method create
3941 	 * @param {String} s Class name, inheritage and prefix.
3942 	 * @param {Object} p Collection of methods to add to the class.
3943 	 * @param {Object} root Optional root object defaults to the global window object.
3944 	 * @example
3945 	 * // Creates a basic class
3946 	 * tinymce.create('tinymce.somepackage.SomeClass', {
3947 	 *     SomeClass: function() {
3948 	 *         // Class constructor
3949 	 *     },
3950 	 *
3951 	 *     method: function() {
3952 	 *         // Some method
3953 	 *     }
3954 	 * });
3955 	 *
3956 	 * // Creates a basic subclass class
3957 	 * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', {
3958 	 *     SomeSubClass: function() {
3959 	 *         // Class constructor
3960 	 *         this.parent(); // Call parent constructor
3961 	 *     },
3962 	 *
3963 	 *     method: function() {
3964 	 *         // Some method
3965 	 *         this.parent(); // Call parent method
3966 	 *     },
3967 	 *
3968 	 *     'static': {
3969 	 *         staticMethod: function() {
3970 	 *             // Static method
3971 	 *         }
3972 	 *     }
3973 	 * });
3974 	 *
3975 	 * // Creates a singleton/static class
3976 	 * tinymce.create('static tinymce.somepackage.SomeSingletonClass', {
3977 	 *     method: function() {
3978 	 *         // Some method
3979 	 *     }
3980 	 * });
3981 	 */
3982 	function create(s, p, root) {
3983 		var self = this, sp, ns, cn, scn, c, de = 0;
3984 
3985 		// Parse : <prefix> <class>:<super class>
3986 		s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
3987 		cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
3988 
3989 		// Create namespace for new class
3990 		ns = self.createNS(s[3].replace(/\.\w+$/, ''), root);
3991 
3992 		// Class already exists
3993 		if (ns[cn]) {
3994 			return;
3995 		}
3996 
3997 		// Make pure static class
3998 		if (s[2] == 'static') {
3999 			ns[cn] = p;
4000 
4001 			if (this.onCreate) {
4002 				this.onCreate(s[2], s[3], ns[cn]);
4003 			}
4004 
4005 			return;
4006 		}
4007 
4008 		// Create default constructor
4009 		if (!p[cn]) {
4010 			p[cn] = function() {};
4011 			de = 1;
4012 		}
4013 
4014 		// Add constructor and methods
4015 		ns[cn] = p[cn];
4016 		self.extend(ns[cn].prototype, p);
4017 
4018 		// Extend
4019 		if (s[5]) {
4020 			sp = self.resolve(s[5]).prototype;
4021 			scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
4022 
4023 			// Extend constructor
4024 			c = ns[cn];
4025 			if (de) {
4026 				// Add passthrough constructor
4027 				ns[cn] = function() {
4028 					return sp[scn].apply(this, arguments);
4029 				};
4030 			} else {
4031 				// Add inherit constructor
4032 				ns[cn] = function() {
4033 					this.parent = sp[scn];
4034 					return c.apply(this, arguments);
4035 				};
4036 			}
4037 			ns[cn].prototype[cn] = ns[cn];
4038 
4039 			// Add super methods
4040 			self.each(sp, function(f, n) {
4041 				ns[cn].prototype[n] = sp[n];
4042 			});
4043 
4044 			// Add overridden methods
4045 			self.each(p, function(f, n) {
4046 				// Extend methods if needed
4047 				if (sp[n]) {
4048 					ns[cn].prototype[n] = function() {
4049 						this.parent = sp[n];
4050 						return f.apply(this, arguments);
4051 					};
4052 				} else {
4053 					if (n != cn) {
4054 						ns[cn].prototype[n] = f;
4055 					}
4056 				}
4057 			});
4058 		}
4059 
4060 		// Add static methods
4061 		/*jshint sub:true*/
4062 		self.each(p['static'], function(f, n) {
4063 			ns[cn][n] = f;
4064 		});
4065 	}
4066 
4067 	/**
4068 	 * Returns the index of a value in an array, this method will return -1 if the item wasn't found.
4069 	 *
4070 	 * @method inArray
4071 	 * @param {Array} a Array/Object to search for value in.
4072 	 * @param {Object} v Value to check for inside the array.
4073 	 * @return {Number/String} Index of item inside the array inside an object. Or -1 if it wasn't found.
4074 	 * @example
4075 	 * // Get index of value in array this will alert 1 since 2 is at that index
4076 	 * alert(tinymce.inArray([1,2,3], 2));
4077 	 */
4078 	function inArray(a, v) {
4079 		var i, l;
4080 
4081 		if (a) {
4082 			for (i = 0, l = a.length; i < l; i++) {
4083 				if (a[i] === v) {
4084 					return i;
4085 				}
4086 			}
4087 		}
4088 
4089 		return -1;
4090 	}
4091 
4092 	function extend(obj, ext) {
4093 		var i, l, name, args = arguments, value;
4094 
4095 		for (i = 1, l = args.length; i < l; i++) {
4096 			ext = args[i];
4097 			for (name in ext) {
4098 				if (ext.hasOwnProperty(name)) {
4099 					value = ext[name];
4100 
4101 					if (value !== undefined) {
4102 						obj[name] = value;
4103 					}
4104 				}
4105 			}
4106 		}
4107 
4108 		return obj;
4109 	}
4110 
4111 	/**
4112 	 * Executed the specified function for each item in a object tree.
4113 	 *
4114 	 * @method walk
4115 	 * @param {Object} o Object tree to walk though.
4116 	 * @param {function} f Function to call for each item.
4117 	 * @param {String} n Optional name of collection inside the objects to walk for example childNodes.
4118 	 * @param {String} s Optional scope to execute the function in.
4119 	 */
4120 	function walk(o, f, n, s) {
4121 		s = s || this;
4122 
4123 		if (o) {
4124 			if (n) {
4125 				o = o[n];
4126 			}
4127 
4128 			each(o, function(o, i) {
4129 				if (f.call(s, o, i, n) === false) {
4130 					return false;
4131 				}
4132 
4133 				walk(o, f, n, s);
4134 			});
4135 		}
4136 	}
4137 
4138 	/**
4139 	 * Creates a namespace on a specific object.
4140 	 *
4141 	 * @method createNS
4142 	 * @param {String} n Namespace to create for example a.b.c.d.
4143 	 * @param {Object} o Optional object to add namespace to, defaults to window.
4144 	 * @return {Object} New namespace object the last item in path.
4145 	 * @example
4146 	 * // Create some namespace
4147 	 * tinymce.createNS('tinymce.somepackage.subpackage');
4148 	 *
4149 	 * // Add a singleton
4150 	 * var tinymce.somepackage.subpackage.SomeSingleton = {
4151 	 *     method: function() {
4152 	 *         // Some method
4153 	 *     }
4154 	 * };
4155 	 */
4156 	function createNS(n, o) {
4157 		var i, v;
4158 
4159 		o = o || window;
4160 
4161 		n = n.split('.');
4162 		for (i = 0; i < n.length; i++) {
4163 			v = n[i];
4164 
4165 			if (!o[v]) {
4166 				o[v] = {};
4167 			}
4168 
4169 			o = o[v];
4170 		}
4171 
4172 		return o;
4173 	}
4174 
4175 	/**
4176 	 * Resolves a string and returns the object from a specific structure.
4177 	 *
4178 	 * @method resolve
4179 	 * @param {String} n Path to resolve for example a.b.c.d.
4180 	 * @param {Object} o Optional object to search though, defaults to window.
4181 	 * @return {Object} Last object in path or null if it couldn't be resolved.
4182 	 * @example
4183 	 * // Resolve a path into an object reference
4184 	 * var obj = tinymce.resolve('a.b.c.d');
4185 	 */
4186 	function resolve(n, o) {
4187 		var i, l;
4188 
4189 		o = o || window;
4190 
4191 		n = n.split('.');
4192 		for (i = 0, l = n.length; i < l; i++) {
4193 			o = o[n[i]];
4194 
4195 			if (!o) {
4196 				break;
4197 			}
4198 		}
4199 
4200 		return o;
4201 	}
4202 
4203 	/**
4204 	 * Splits a string but removes the whitespace before and after each value.
4205 	 *
4206 	 * @method explode
4207 	 * @param {string} s String to split.
4208 	 * @param {string} d Delimiter to split by.
4209 	 * @example
4210 	 * // Split a string into an array with a,b,c
4211 	 * var arr = tinymce.explode('a, b,   c');
4212 	 */
4213 	function explode(s, d) {
4214 		if (!s || is(s, 'array')) {
4215 			return s;
4216 		}
4217 
4218 		return map(s.split(d || ','), trim);
4219 	}
4220 
4221 	return {
4222 		trim: trim,
4223 		isArray: isArray,
4224 		is: is,
4225 		toArray: toArray,
4226 		makeMap: makeMap,
4227 		each: each,
4228 		map: map,
4229 		grep: grep,
4230 		inArray: inArray,
4231 		extend: extend,
4232 		create: create,
4233 		walk: walk,
4234 		createNS: createNS,
4235 		resolve: resolve,
4236 		explode: explode
4237 	};
4238 });
4239 
4240 // Included from: js/tinymce/classes/dom/Range.js
4241 
4242 /**
4243  * Range.js
4244  *
4245  * Copyright, Moxiecode Systems AB
4246  * Released under LGPL License.
4247  *
4248  * License: http://www.tinymce.com/license
4249  * Contributing: http://www.tinymce.com/contributing
4250  */
4251 
4252 define("tinymce/dom/Range", [
4253 	"tinymce/util/Tools"
4254 ], function(Tools) {
4255 	// Range constructor
4256 	function Range(dom) {
4257 		var self = this,
4258 			doc = dom.doc,
4259 			EXTRACT = 0,
4260 			CLONE = 1,
4261 			DELETE = 2,
4262 			TRUE = true,
4263 			FALSE = false,
4264 			START_OFFSET = 'startOffset',
4265 			START_CONTAINER = 'startContainer',
4266 			END_CONTAINER = 'endContainer',
4267 			END_OFFSET = 'endOffset',
4268 			extend = Tools.extend,
4269 			nodeIndex = dom.nodeIndex;
4270 
4271 		function createDocumentFragment() {
4272 			return doc.createDocumentFragment();
4273 		}
4274 
4275 		function setStart(n, o) {
4276 			_setEndPoint(TRUE, n, o);
4277 		}
4278 
4279 		function setEnd(n, o) {
4280 			_setEndPoint(FALSE, n, o);
4281 		}
4282 
4283 		function setStartBefore(n) {
4284 			setStart(n.parentNode, nodeIndex(n));
4285 		}
4286 
4287 		function setStartAfter(n) {
4288 			setStart(n.parentNode, nodeIndex(n) + 1);
4289 		}
4290 
4291 		function setEndBefore(n) {
4292 			setEnd(n.parentNode, nodeIndex(n));
4293 		}
4294 
4295 		function setEndAfter(n) {
4296 			setEnd(n.parentNode, nodeIndex(n) + 1);
4297 		}
4298 
4299 		function collapse(ts) {
4300 			if (ts) {
4301 				self[END_CONTAINER] = self[START_CONTAINER];
4302 				self[END_OFFSET] = self[START_OFFSET];
4303 			} else {
4304 				self[START_CONTAINER] = self[END_CONTAINER];
4305 				self[START_OFFSET] = self[END_OFFSET];
4306 			}
4307 
4308 			self.collapsed = TRUE;
4309 		}
4310 
4311 		function selectNode(n) {
4312 			setStartBefore(n);
4313 			setEndAfter(n);
4314 		}
4315 
4316 		function selectNodeContents(n) {
4317 			setStart(n, 0);
4318 			setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
4319 		}
4320 
4321 		function compareBoundaryPoints(h, r) {
4322 			var sc = self[START_CONTAINER], so = self[START_OFFSET], ec = self[END_CONTAINER], eo = self[END_OFFSET],
4323 			rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
4324 
4325 			// Check START_TO_START
4326 			if (h === 0) {
4327 				return _compareBoundaryPoints(sc, so, rsc, rso);
4328 			}
4329 
4330 			// Check START_TO_END
4331 			if (h === 1) {
4332 				return _compareBoundaryPoints(ec, eo, rsc, rso);
4333 			}
4334 
4335 			// Check END_TO_END
4336 			if (h === 2) {
4337 				return _compareBoundaryPoints(ec, eo, rec, reo);
4338 			}
4339 
4340 			// Check END_TO_START
4341 			if (h === 3) {
4342 				return _compareBoundaryPoints(sc, so, rec, reo);
4343 			}
4344 		}
4345 
4346 		function deleteContents() {
4347 			_traverse(DELETE);
4348 		}
4349 
4350 		function extractContents() {
4351 			return _traverse(EXTRACT);
4352 		}
4353 
4354 		function cloneContents() {
4355 			return _traverse(CLONE);
4356 		}
4357 
4358 		function insertNode(n) {
4359 			var startContainer = this[START_CONTAINER],
4360 				startOffset = this[START_OFFSET], nn, o;
4361 
4362 			// Node is TEXT_NODE or CDATA
4363 			if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
4364 				if (!startOffset) {
4365 					// At the start of text
4366 					startContainer.parentNode.insertBefore(n, startContainer);
4367 				} else if (startOffset >= startContainer.nodeValue.length) {
4368 					// At the end of text
4369 					dom.insertAfter(n, startContainer);
4370 				} else {
4371 					// Middle, need to split
4372 					nn = startContainer.splitText(startOffset);
4373 					startContainer.parentNode.insertBefore(n, nn);
4374 				}
4375 			} else {
4376 				// Insert element node
4377 				if (startContainer.childNodes.length > 0) {
4378 					o = startContainer.childNodes[startOffset];
4379 				}
4380 
4381 				if (o) {
4382 					startContainer.insertBefore(n, o);
4383 				} else {
4384 					if (startContainer.nodeType == 3) {
4385 						dom.insertAfter(n, startContainer);
4386 					} else {
4387 						startContainer.appendChild(n);
4388 					}
4389 				}
4390 			}
4391 		}
4392 
4393 		function surroundContents(n) {
4394 			var f = self.extractContents();
4395 
4396 			self.insertNode(n);
4397 			n.appendChild(f);
4398 			self.selectNode(n);
4399 		}
4400 
4401 		function cloneRange() {
4402 			return extend(new Range(dom), {
4403 				startContainer: self[START_CONTAINER],
4404 				startOffset: self[START_OFFSET],
4405 				endContainer: self[END_CONTAINER],
4406 				endOffset: self[END_OFFSET],
4407 				collapsed: self.collapsed,
4408 				commonAncestorContainer: self.commonAncestorContainer
4409 			});
4410 		}
4411 
4412 		// Private methods
4413 
4414 		function _getSelectedNode(container, offset) {
4415 			var child;
4416 
4417 			if (container.nodeType == 3 /* TEXT_NODE */) {
4418 				return container;
4419 			}
4420 
4421 			if (offset < 0) {
4422 				return container;
4423 			}
4424 
4425 			child = container.firstChild;
4426 			while (child && offset > 0) {
4427 				--offset;
4428 				child = child.nextSibling;
4429 			}
4430 
4431 			if (child) {
4432 				return child;
4433 			}
4434 
4435 			return container;
4436 		}
4437 
4438 		function _isCollapsed() {
4439 			return (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[END_OFFSET]);
4440 		}
4441 
4442 		function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
4443 			var c, offsetC, n, cmnRoot, childA, childB;
4444 
4445 			// In the first case the boundary-points have the same container. A is before B
4446 			// if its offset is less than the offset of B, A is equal to B if its offset is
4447 			// equal to the offset of B, and A is after B if its offset is greater than the
4448 			// offset of B.
4449 			if (containerA == containerB) {
4450 				if (offsetA == offsetB) {
4451 					return 0; // equal
4452 				}
4453 
4454 				if (offsetA < offsetB) {
4455 					return -1; // before
4456 				}
4457 
4458 				return 1; // after
4459 			}
4460 
4461 			// In the second case a child node C of the container of A is an ancestor
4462 			// container of B. In this case, A is before B if the offset of A is less than or
4463 			// equal to the index of the child node C and A is after B otherwise.
4464 			c = containerB;
4465 			while (c && c.parentNode != containerA) {
4466 				c = c.parentNode;
4467 			}
4468 
4469 			if (c) {
4470 				offsetC = 0;
4471 				n = containerA.firstChild;
4472 
4473 				while (n != c && offsetC < offsetA) {
4474 					offsetC++;
4475 					n = n.nextSibling;
4476 				}
4477 
4478 				if (offsetA <= offsetC) {
4479 					return -1; // before
4480 				}
4481 
4482 				return 1; // after
4483 			}
4484 
4485 			// In the third case a child node C of the container of B is an ancestor container
4486 			// of A. In this case, A is before B if the index of the child node C is less than
4487 			// the offset of B and A is after B otherwise.
4488 			c = containerA;
4489 			while (c && c.parentNode != containerB) {
4490 				c = c.parentNode;
4491 			}
4492 
4493 			if (c) {
4494 				offsetC = 0;
4495 				n = containerB.firstChild;
4496 
4497 				while (n != c && offsetC < offsetB) {
4498 					offsetC++;
4499 					n = n.nextSibling;
4500 				}
4501 
4502 				if (offsetC < offsetB) {
4503 					return -1; // before
4504 				}
4505 
4506 				return 1; // after
4507 			}
4508 
4509 			// In the fourth case, none of three other cases hold: the containers of A and B
4510 			// are siblings or descendants of sibling nodes. In this case, A is before B if
4511 			// the container of A is before the container of B in a pre-order traversal of the
4512 			// Ranges' context tree and A is after B otherwise.
4513 			cmnRoot = dom.findCommonAncestor(containerA, containerB);
4514 			childA = containerA;
4515 
4516 			while (childA && childA.parentNode != cmnRoot) {
4517 				childA = childA.parentNode;
4518 			}
4519 
4520 			if (!childA) {
4521 				childA = cmnRoot;
4522 			}
4523 
4524 			childB = containerB;
4525 			while (childB && childB.parentNode != cmnRoot) {
4526 				childB = childB.parentNode;
4527 			}
4528 
4529 			if (!childB) {
4530 				childB = cmnRoot;
4531 			}
4532 
4533 			if (childA == childB) {
4534 				return 0; // equal
4535 			}
4536 
4537 			n = cmnRoot.firstChild;
4538 			while (n) {
4539 				if (n == childA) {
4540 					return -1; // before
4541 				}
4542 
4543 				if (n == childB) {
4544 					return 1; // after
4545 				}
4546 
4547 				n = n.nextSibling;
4548 			}
4549 		}
4550 
4551 		function _setEndPoint(st, n, o) {
4552 			var ec, sc;
4553 
4554 			if (st) {
4555 				self[START_CONTAINER] = n;
4556 				self[START_OFFSET] = o;
4557 			} else {
4558 				self[END_CONTAINER] = n;
4559 				self[END_OFFSET] = o;
4560 			}
4561 
4562 			// If one boundary-point of a Range is set to have a root container
4563 			// other than the current one for the Range, the Range is collapsed to
4564 			// the new position. This enforces the restriction that both boundary-
4565 			// points of a Range must have the same root container.
4566 			ec = self[END_CONTAINER];
4567 			while (ec.parentNode) {
4568 				ec = ec.parentNode;
4569 			}
4570 
4571 			sc = self[START_CONTAINER];
4572 			while (sc.parentNode) {
4573 				sc = sc.parentNode;
4574 			}
4575 
4576 			if (sc == ec) {
4577 				// The start position of a Range is guaranteed to never be after the
4578 				// end position. To enforce this restriction, if the start is set to
4579 				// be at a position after the end, the Range is collapsed to that
4580 				// position.
4581 				if (_compareBoundaryPoints(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) {
4582 					self.collapse(st);
4583 				}
4584 			} else {
4585 				self.collapse(st);
4586 			}
4587 
4588 			self.collapsed = _isCollapsed();
4589 			self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]);
4590 		}
4591 
4592 		function _traverse(how) {
4593 			var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
4594 
4595 			if (self[START_CONTAINER] == self[END_CONTAINER]) {
4596 				return _traverseSameContainer(how);
4597 			}
4598 
4599 			for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
4600 				if (p == self[START_CONTAINER]) {
4601 					return _traverseCommonStartContainer(c, how);
4602 				}
4603 
4604 				++endContainerDepth;
4605 			}
4606 
4607 			for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
4608 				if (p == self[END_CONTAINER]) {
4609 					return _traverseCommonEndContainer(c, how);
4610 				}
4611 
4612 				++startContainerDepth;
4613 			}
4614 
4615 			depthDiff = startContainerDepth - endContainerDepth;
4616 
4617 			startNode = self[START_CONTAINER];
4618 			while (depthDiff > 0) {
4619 				startNode = startNode.parentNode;
4620 				depthDiff--;
4621 			}
4622 
4623 			endNode = self[END_CONTAINER];
4624 			while (depthDiff < 0) {
4625 				endNode = endNode.parentNode;
4626 				depthDiff++;
4627 			}
4628 
4629 			// ascend the ancestor hierarchy until we have a common parent.
4630 			for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
4631 				startNode = sp;
4632 				endNode = ep;
4633 			}
4634 
4635 			return _traverseCommonAncestors(startNode, endNode, how);
4636 		}
4637 
4638 		function _traverseSameContainer(how) {
4639 			var frag, s, sub, n, cnt, sibling, xferNode, start, len;
4640 
4641 			if (how != DELETE) {
4642 				frag = createDocumentFragment();
4643 			}
4644 
4645 			// If selection is empty, just return the fragment
4646 			if (self[START_OFFSET] == self[END_OFFSET]) {
4647 				return frag;
4648 			}
4649 
4650 			// Text node needs special case handling
4651 			if (self[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {
4652 				// get the substring
4653 				s = self[START_CONTAINER].nodeValue;
4654 				sub = s.substring(self[START_OFFSET], self[END_OFFSET]);
4655 
4656 				// set the original text node to its new value
4657 				if (how != CLONE) {
4658 					n = self[START_CONTAINER];
4659 					start = self[START_OFFSET];
4660 					len = self[END_OFFSET] - self[START_OFFSET];
4661 
4662 					if (start === 0 && len >= n.nodeValue.length - 1) {
4663 						n.parentNode.removeChild(n);
4664 					} else {
4665 						n.deleteData(start, len);
4666 					}
4667 
4668 					// Nothing is partially selected, so collapse to start point
4669 					self.collapse(TRUE);
4670 				}
4671 
4672 				if (how == DELETE) {
4673 					return;
4674 				}
4675 
4676 				if (sub.length > 0) {
4677 					frag.appendChild(doc.createTextNode(sub));
4678 				}
4679 
4680 				return frag;
4681 			}
4682 
4683 			// Copy nodes between the start/end offsets.
4684 			n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]);
4685 			cnt = self[END_OFFSET] - self[START_OFFSET];
4686 
4687 			while (n && cnt > 0) {
4688 				sibling = n.nextSibling;
4689 				xferNode = _traverseFullySelected(n, how);
4690 
4691 				if (frag) {
4692 					frag.appendChild(xferNode);
4693 				}
4694 
4695 				--cnt;
4696 				n = sibling;
4697 			}
4698 
4699 			// Nothing is partially selected, so collapse to start point
4700 			if (how != CLONE) {
4701 				self.collapse(TRUE);
4702 			}
4703 
4704 			return frag;
4705 		}
4706 
4707 		function _traverseCommonStartContainer(endAncestor, how) {
4708 			var frag, n, endIdx, cnt, sibling, xferNode;
4709 
4710 			if (how != DELETE) {
4711 				frag = createDocumentFragment();
4712 			}
4713 
4714 			n = _traverseRightBoundary(endAncestor, how);
4715 
4716 			if (frag) {
4717 				frag.appendChild(n);
4718 			}
4719 
4720 			endIdx = nodeIndex(endAncestor);
4721 			cnt = endIdx - self[START_OFFSET];
4722 
4723 			if (cnt <= 0) {
4724 				// Collapse to just before the endAncestor, which
4725 				// is partially selected.
4726 				if (how != CLONE) {
4727 					self.setEndBefore(endAncestor);
4728 					self.collapse(FALSE);
4729 				}
4730 
4731 				return frag;
4732 			}
4733 
4734 			n = endAncestor.previousSibling;
4735 			while (cnt > 0) {
4736 				sibling = n.previousSibling;
4737 				xferNode = _traverseFullySelected(n, how);
4738 
4739 				if (frag) {
4740 					frag.insertBefore(xferNode, frag.firstChild);
4741 				}
4742 
4743 				--cnt;
4744 				n = sibling;
4745 			}
4746 
4747 			// Collapse to just before the endAncestor, which
4748 			// is partially selected.
4749 			if (how != CLONE) {
4750 				self.setEndBefore(endAncestor);
4751 				self.collapse(FALSE);
4752 			}
4753 
4754 			return frag;
4755 		}
4756 
4757 		function _traverseCommonEndContainer(startAncestor, how) {
4758 			var frag, startIdx, n, cnt, sibling, xferNode;
4759 
4760 			if (how != DELETE) {
4761 				frag = createDocumentFragment();
4762 			}
4763 
4764 			n = _traverseLeftBoundary(startAncestor, how);
4765 			if (frag) {
4766 				frag.appendChild(n);
4767 			}
4768 
4769 			startIdx = nodeIndex(startAncestor);
4770 			++startIdx; // Because we already traversed it
4771 
4772 			cnt = self[END_OFFSET] - startIdx;
4773 			n = startAncestor.nextSibling;
4774 			while (n && cnt > 0) {
4775 				sibling = n.nextSibling;
4776 				xferNode = _traverseFullySelected(n, how);
4777 
4778 				if (frag) {
4779 					frag.appendChild(xferNode);
4780 				}
4781 
4782 				--cnt;
4783 				n = sibling;
4784 			}
4785 
4786 			if (how != CLONE) {
4787 				self.setStartAfter(startAncestor);
4788 				self.collapse(TRUE);
4789 			}
4790 
4791 			return frag;
4792 		}
4793 
4794 		function _traverseCommonAncestors(startAncestor, endAncestor, how) {
4795 			var n, frag, startOffset, endOffset, cnt, sibling, nextSibling;
4796 
4797 			if (how != DELETE) {
4798 				frag = createDocumentFragment();
4799 			}
4800 
4801 			n = _traverseLeftBoundary(startAncestor, how);
4802 			if (frag) {
4803 				frag.appendChild(n);
4804 			}
4805 
4806 			startOffset = nodeIndex(startAncestor);
4807 			endOffset = nodeIndex(endAncestor);
4808 			++startOffset;
4809 
4810 			cnt = endOffset - startOffset;
4811 			sibling = startAncestor.nextSibling;
4812 
4813 			while (cnt > 0) {
4814 				nextSibling = sibling.nextSibling;
4815 				n = _traverseFullySelected(sibling, how);
4816 
4817 				if (frag) {
4818 					frag.appendChild(n);
4819 				}
4820 
4821 				sibling = nextSibling;
4822 				--cnt;
4823 			}
4824 
4825 			n = _traverseRightBoundary(endAncestor, how);
4826 
4827 			if (frag) {
4828 				frag.appendChild(n);
4829 			}
4830 
4831 			if (how != CLONE) {
4832 				self.setStartAfter(startAncestor);
4833 				self.collapse(TRUE);
4834 			}
4835 
4836 			return frag;
4837 		}
4838 
4839 		function _traverseRightBoundary(root, how) {
4840 			var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), parent, clonedParent;
4841 			var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[END_CONTAINER];
4842 
4843 			if (next == root) {
4844 				return _traverseNode(next, isFullySelected, FALSE, how);
4845 			}
4846 
4847 			parent = next.parentNode;
4848 			clonedParent = _traverseNode(parent, FALSE, FALSE, how);
4849 
4850 			while (parent) {
4851 				while (next) {
4852 					prevSibling = next.previousSibling;
4853 					clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
4854 
4855 					if (how != DELETE) {
4856 						clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
4857 					}
4858 
4859 					isFullySelected = TRUE;
4860 					next = prevSibling;
4861 				}
4862 
4863 				if (parent == root) {
4864 					return clonedParent;
4865 				}
4866 
4867 				next = parent.previousSibling;
4868 				parent = parent.parentNode;
4869 
4870 				clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
4871 
4872 				if (how != DELETE) {
4873 					clonedGrandParent.appendChild(clonedParent);
4874 				}
4875 
4876 				clonedParent = clonedGrandParent;
4877 			}
4878 		}
4879 
4880 		function _traverseLeftBoundary(root, how) {
4881 			var next = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]), isFullySelected = next != self[START_CONTAINER];
4882 			var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
4883 
4884 			if (next == root) {
4885 				return _traverseNode(next, isFullySelected, TRUE, how);
4886 			}
4887 
4888 			parent = next.parentNode;
4889 			clonedParent = _traverseNode(parent, FALSE, TRUE, how);
4890 
4891 			while (parent) {
4892 				while (next) {
4893 					nextSibling = next.nextSibling;
4894 					clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
4895 
4896 					if (how != DELETE) {
4897 						clonedParent.appendChild(clonedChild);
4898 					}
4899 
4900 					isFullySelected = TRUE;
4901 					next = nextSibling;
4902 				}
4903 
4904 				if (parent == root) {
4905 					return clonedParent;
4906 				}
4907 
4908 				next = parent.nextSibling;
4909 				parent = parent.parentNode;
4910 
4911 				clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
4912 
4913 				if (how != DELETE) {
4914 					clonedGrandParent.appendChild(clonedParent);
4915 				}
4916 
4917 				clonedParent = clonedGrandParent;
4918 			}
4919 		}
4920 
4921 		function _traverseNode(n, isFullySelected, isLeft, how) {
4922 			var txtValue, newNodeValue, oldNodeValue, offset, newNode;
4923 
4924 			if (isFullySelected) {
4925 				return _traverseFullySelected(n, how);
4926 			}
4927 
4928 			if (n.nodeType == 3 /* TEXT_NODE */) {
4929 				txtValue = n.nodeValue;
4930 
4931 				if (isLeft) {
4932 					offset = self[START_OFFSET];
4933 					newNodeValue = txtValue.substring(offset);
4934 					oldNodeValue = txtValue.substring(0, offset);
4935 				} else {
4936 					offset = self[END_OFFSET];
4937 					newNodeValue = txtValue.substring(0, offset);
4938 					oldNodeValue = txtValue.substring(offset);
4939 				}
4940 
4941 				if (how != CLONE) {
4942 					n.nodeValue = oldNodeValue;
4943 				}
4944 
4945 				if (how == DELETE) {
4946 					return;
4947 				}
4948 
4949 				newNode = dom.clone(n, FALSE);
4950 				newNode.nodeValue = newNodeValue;
4951 
4952 				return newNode;
4953 			}
4954 
4955 			if (how == DELETE) {
4956 				return;
4957 			}
4958 
4959 			return dom.clone(n, FALSE);
4960 		}
4961 
4962 		function _traverseFullySelected(n, how) {
4963 			if (how != DELETE) {
4964 				return how == CLONE ? dom.clone(n, TRUE) : n;
4965 			}
4966 
4967 			n.parentNode.removeChild(n);
4968 		}
4969 
4970 		function toStringIE() {
4971 			return dom.create('body', null, cloneContents()).outerText;
4972 		}
4973 
4974 		extend(self, {
4975 			// Inital states
4976 			startContainer: doc,
4977 			startOffset: 0,
4978 			endContainer: doc,
4979 			endOffset: 0,
4980 			collapsed: TRUE,
4981 			commonAncestorContainer: doc,
4982 
4983 			// Range constants
4984 			START_TO_START: 0,
4985 			START_TO_END: 1,
4986 			END_TO_END: 2,
4987 			END_TO_START: 3,
4988 
4989 			// Public methods
4990 			setStart: setStart,
4991 			setEnd: setEnd,
4992 			setStartBefore: setStartBefore,
4993 			setStartAfter: setStartAfter,
4994 			setEndBefore: setEndBefore,
4995 			setEndAfter: setEndAfter,
4996 			collapse: collapse,
4997 			selectNode: selectNode,
4998 			selectNodeContents: selectNodeContents,
4999 			compareBoundaryPoints: compareBoundaryPoints,
5000 			deleteContents: deleteContents,
5001 			extractContents: extractContents,
5002 			cloneContents: cloneContents,
5003 			insertNode: insertNode,
5004 			surroundContents: surroundContents,
5005 			cloneRange: cloneRange,
5006 			toStringIE: toStringIE
5007 		});
5008 
5009 		return self;
5010 	}
5011 
5012 	// Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
5013 	Range.prototype.toString = function() {
5014 		return this.toStringIE();
5015 	};
5016 
5017 	return Range;
5018 });
5019 
5020 // Included from: js/tinymce/classes/html/Entities.js
5021 
5022 /**
5023  * Entities.js
5024  *
5025  * Copyright, Moxiecode Systems AB
5026  * Released under LGPL License.
5027  *
5028  * License: http://www.tinymce.com/license
5029  * Contributing: http://www.tinymce.com/contributing
5030  */
5031 
5032 /*jshint bitwise:false */
5033 /*eslint no-bitwise:0 */
5034 
5035 /**
5036  * Entity encoder class.
5037  *
5038  * @class tinymce.html.Entities
5039  * @static
5040  * @version 3.4
5041  */
5042 define("tinymce/html/Entities", [
5043 	"tinymce/util/Tools"
5044 ], function(Tools) {
5045 	var makeMap = Tools.makeMap;
5046 
5047 	var namedEntities, baseEntities, reverseEntities,
5048 		attrsCharsRegExp = /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
5049 		textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
5050 		rawCharsRegExp = /[<>&\"\']/g,
5051 		entityRegExp = /&(#x|#)?([\w]+);/g,
5052 		asciiMap = {
5053 			128: "\u20AC", 130: "\u201A", 131: "\u0192", 132: "\u201E", 133: "\u2026", 134: "\u2020",
5054 			135: "\u2021", 136: "\u02C6", 137: "\u2030", 138: "\u0160", 139: "\u2039", 140: "\u0152",
5055 			142: "\u017D", 145: "\u2018", 146: "\u2019", 147: "\u201C", 148: "\u201D", 149: "\u2022",
5056 			150: "\u2013", 151: "\u2014", 152: "\u02DC", 153: "\u2122", 154: "\u0161", 155: "\u203A",
5057 			156: "\u0153", 158: "\u017E", 159: "\u0178"
5058 		};
5059 
5060 	// Raw entities
5061 	baseEntities = {
5062 		'\"': '"', // Needs to be escaped since the YUI compressor would otherwise break the code
5063 		"'": ''',
5064 		'<': '<',
5065 		'>': '>',
5066 		'&': '&',
5067 		'\u0060': '`'
5068 	};
5069 
5070 	// Reverse lookup table for raw entities
5071 	reverseEntities = {
5072 		'<': '<',
5073 		'>': '>',
5074 		'&': '&',
5075 		'"': '"',
5076 		''': "'"
5077 	};
5078 
5079 	// Decodes text by using the browser
5080 	function nativeDecode(text) {
5081 		var elm;
5082 
5083 		elm = document.createElement("div");
5084 		elm.innerHTML = text;
5085 
5086 		return elm.textContent || elm.innerText || text;
5087 	}
5088 
5089 	// Build a two way lookup table for the entities
5090 	function buildEntitiesLookup(items, radix) {
5091 		var i, chr, entity, lookup = {};
5092 
5093 		if (items) {
5094 			items = items.split(',');
5095 			radix = radix || 10;
5096 
5097 			// Build entities lookup table
5098 			for (i = 0; i < items.length; i += 2) {
5099 				chr = String.fromCharCode(parseInt(items[i], radix));
5100 
5101 				// Only add non base entities
5102 				if (!baseEntities[chr]) {
5103 					entity = '&' + items[i + 1] + ';';
5104 					lookup[chr] = entity;
5105 					lookup[entity] = chr;
5106 				}
5107 			}
5108 
5109 			return lookup;
5110 		}
5111 	}
5112 
5113 	// Unpack entities lookup where the numbers are in radix 32 to reduce the size
5114 	namedEntities = buildEntitiesLookup(
5115 		'50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
5116 		'5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
5117 		'5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
5118 		'5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
5119 		'68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
5120 		'6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
5121 		'6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
5122 		'75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
5123 		'7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
5124 		'7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
5125 		'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
5126 		'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
5127 		't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
5128 		'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
5129 		'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
5130 		'81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
5131 		'8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
5132 		'8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
5133 		'8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
5134 		'8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
5135 		'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
5136 		'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
5137 		'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
5138 		'80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
5139 		'811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
5140 
5141 	var Entities = {
5142 		/**
5143 		 * Encodes the specified string using raw entities. This means only the required XML base entities will be endoded.
5144 		 *
5145 		 * @method encodeRaw
5146 		 * @param {String} text Text to encode.
5147 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
5148 		 * @return {String} Entity encoded text.
5149 		 */
5150 		encodeRaw: function(text, attr) {
5151 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
5152 				return baseEntities[chr] || chr;
5153 			});
5154 		},
5155 
5156 		/**
5157 		 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
5158 		 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
5159 		 * and is exposed as the DOMUtils.encode function.
5160 		 *
5161 		 * @method encodeAllRaw
5162 		 * @param {String} text Text to encode.
5163 		 * @return {String} Entity encoded text.
5164 		 */
5165 		encodeAllRaw: function(text) {
5166 			return ('' + text).replace(rawCharsRegExp, function(chr) {
5167 				return baseEntities[chr] || chr;
5168 			});
5169 		},
5170 
5171 		/**
5172 		 * Encodes the specified string using numeric entities. The core entities will be
5173 		 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
5174 		 *
5175 		 * @method encodeNumeric
5176 		 * @param {String} text Text to encode.
5177 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
5178 		 * @return {String} Entity encoded text.
5179 		 */
5180 		encodeNumeric: function(text, attr) {
5181 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
5182 				// Multi byte sequence convert it to a single entity
5183 				if (chr.length > 1) {
5184 					return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
5185 				}
5186 
5187 				return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
5188 			});
5189 		},
5190 
5191 		/**
5192 		 * Encodes the specified string using named entities. The core entities will be encoded
5193 		 * as named ones but all non lower ascii characters will be encoded into named entities.
5194 		 *
5195 		 * @method encodeNamed
5196 		 * @param {String} text Text to encode.
5197 		 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
5198 		 * @param {Object} entities Optional parameter with entities to use.
5199 		 * @return {String} Entity encoded text.
5200 		 */
5201 		encodeNamed: function(text, attr, entities) {
5202 			entities = entities || namedEntities;
5203 
5204 			return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
5205 				return baseEntities[chr] || entities[chr] || chr;
5206 			});
5207 		},
5208 
5209 		/**
5210 		 * Returns an encode function based on the name(s) and it's optional entities.
5211 		 *
5212 		 * @method getEncodeFunc
5213 		 * @param {String} name Comma separated list of encoders for example named,numeric.
5214 		 * @param {String} entities Optional parameter with entities to use instead of the built in set.
5215 		 * @return {function} Encode function to be used.
5216 		 */
5217 		getEncodeFunc: function(name, entities) {
5218 			entities = buildEntitiesLookup(entities) || namedEntities;
5219 
5220 			function encodeNamedAndNumeric(text, attr) {
5221 				return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
5222 					return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
5223 				});
5224 			}
5225 
5226 			function encodeCustomNamed(text, attr) {
5227 				return Entities.encodeNamed(text, attr, entities);
5228 			}
5229 
5230 			// Replace + with , to be compatible with previous TinyMCE versions
5231 			name = makeMap(name.replace(/\+/g, ','));
5232 
5233 			// Named and numeric encoder
5234 			if (name.named && name.numeric) {
5235 				return encodeNamedAndNumeric;
5236 			}
5237 
5238 			// Named encoder
5239 			if (name.named) {
5240 				// Custom names
5241 				if (entities) {
5242 					return encodeCustomNamed;
5243 				}
5244 
5245 				return Entities.encodeNamed;
5246 			}
5247 
5248 			// Numeric
5249 			if (name.numeric) {
5250 				return Entities.encodeNumeric;
5251 			}
5252 
5253 			// Raw encoder
5254 			return Entities.encodeRaw;
5255 		},
5256 
5257 		/**
5258 		 * Decodes the specified string, this will replace entities with raw UTF characters.
5259 		 *
5260 		 * @method decode
5261 		 * @param {String} text Text to entity decode.
5262 		 * @return {String} Entity decoded string.
5263 		 */
5264 		decode: function(text) {
5265 			return text.replace(entityRegExp, function(all, numeric, value) {
5266 				if (numeric) {
5267 					value = parseInt(value, numeric.length === 2 ? 16 : 10);
5268 
5269 					// Support upper UTF
5270 					if (value > 0xFFFF) {
5271 						value -= 0x10000;
5272 
5273 						return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));
5274 					} else {
5275 						return asciiMap[value] || String.fromCharCode(value);
5276 					}
5277 				}
5278 
5279 				return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
5280 			});
5281 		}
5282 	};
5283 
5284 	return Entities;
5285 });
5286 
5287 // Included from: js/tinymce/classes/Env.js
5288 
5289 /**
5290  * Env.js
5291  *
5292  * Copyright, Moxiecode Systems AB
5293  * Released under LGPL License.
5294  *
5295  * License: http://www.tinymce.com/license
5296  * Contributing: http://www.tinymce.com/contributing
5297  */
5298 
5299 /**
5300  * This class contains various environment constants like browser versions etc.
5301  * Normally you don't want to sniff specific browser versions but sometimes you have
5302  * to when it's impossible to feature detect. So use this with care.
5303  *
5304  * @class tinymce.Env
5305  * @static
5306  */
5307 define("tinymce/Env", [], function() {
5308 	var nav = navigator, userAgent = nav.userAgent;
5309 	var opera, webkit, ie, ie11, gecko, mac, iDevice;
5310 
5311 	opera = window.opera && window.opera.buildNumber;
5312 	webkit = /WebKit/.test(userAgent);
5313 	ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName);
5314 	ie = ie && /MSIE (\w+)\./.exec(userAgent)[1];
5315 	ie11 = userAgent.indexOf('Trident/') != -1 && (userAgent.indexOf('rv:') != -1 || nav.appName.indexOf('Netscape') != -1) ? 11 : false;
5316 	ie = ie || ie11;
5317 	gecko = !webkit && !ie11 && /Gecko/.test(userAgent);
5318 	mac = userAgent.indexOf('Mac') != -1;
5319 	iDevice = /(iPad|iPhone)/.test(userAgent);
5320 
5321 	// Is a iPad/iPhone and not on iOS5 sniff the WebKit version since older iOS WebKit versions
5322 	// says it has contentEditable support but there is no visible caret.
5323 	var contentEditable = !iDevice || userAgent.match(/AppleWebKit\/(\d*)/)[1] >= 534;
5324 
5325 	return {
5326 		/**
5327 		 * Constant that is true if the browser is Opera.
5328 		 *
5329 		 * @property opera
5330 		 * @type Boolean
5331 		 * @final
5332 		 */
5333 		opera: opera,
5334 
5335 		/**
5336 		 * Constant that is true if the browser is WebKit (Safari/Chrome).
5337 		 *
5338 		 * @property webKit
5339 		 * @type Boolean
5340 		 * @final
5341 		 */
5342 		webkit: webkit,
5343 
5344 		/**
5345 		 * Constant that is more than zero if the browser is IE.
5346 		 *
5347 		 * @property ie
5348 		 * @type Boolean
5349 		 * @final
5350 		 */
5351 		ie: ie,
5352 
5353 		/**
5354 		 * Constant that is true if the browser is Gecko.
5355 		 *
5356 		 * @property gecko
5357 		 * @type Boolean
5358 		 * @final
5359 		 */
5360 		gecko: gecko,
5361 
5362 		/**
5363 		 * Constant that is true if the os is Mac OS.
5364 		 *
5365 		 * @property mac
5366 		 * @type Boolean
5367 		 * @final
5368 		 */
5369 		mac: mac,
5370 
5371 		/**
5372 		 * Constant that is true if the os is iOS.
5373 		 *
5374 		 * @property iOS
5375 		 * @type Boolean
5376 		 * @final
5377 		 */
5378 		iOS: iDevice,
5379 
5380 		/**
5381 		 * Constant that is true if the browser supports editing.
5382 		 *
5383 		 * @property contentEditable
5384 		 * @type Boolean
5385 		 * @final
5386 		 */
5387 		contentEditable: contentEditable,
5388 
5389 		/**
5390 		 * Transparent image data url.
5391 		 *
5392 		 * @property transparentSrc
5393 		 * @type Boolean
5394 		 * @final
5395 		 */
5396 		transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
5397 
5398 		/**
5399 		 * Returns true/false if the browser can or can't place the caret after a inline block like an image.
5400 		 *
5401 		 * @property noCaretAfter
5402 		 * @type Boolean
5403 		 * @final
5404 		 */
5405 		caretAfter: ie != 8,
5406 
5407 		/**
5408 		 * Constant that is true if the browser supports native DOM Ranges. IE 9+.
5409 		 *
5410 		 * @property range
5411 		 * @type Boolean
5412 		 */
5413 		range: window.getSelection && "Range" in window,
5414 
5415 		/**
5416 		 * Returns the IE document mode for non IE browsers this will fake IE 10.
5417 		 *
5418 		 * @property documentMode
5419 		 * @type Number
5420 		 */
5421 		documentMode: ie ? (document.documentMode || 7) : 10
5422 	};
5423 });
5424 
5425 // Included from: js/tinymce/classes/dom/StyleSheetLoader.js
5426 
5427 /**
5428  * StyleSheetLoader.js
5429  *
5430  * Copyright, Moxiecode Systems AB
5431  * Released under LGPL License.
5432  *
5433  * License: http://www.tinymce.com/license
5434  * Contributing: http://www.tinymce.com/contributing
5435  */
5436 
5437 /**
5438  * This class handles loading of external stylesheets and fires events when these are loaded.
5439  *
5440  * @class tinymce.dom.StyleSheetLoader
5441  * @private
5442  */
5443 define("tinymce/dom/StyleSheetLoader", [], function() {
5444 	"use strict";
5445 
5446 	return function(document, settings) {
5447 		var idCount = 0, loadedStates = {}, maxLoadTime;
5448 
5449 		settings = settings || {};
5450 		maxLoadTime = settings.maxLoadTime || 5000;
5451 
5452 		function appendToHead(node) {
5453 			document.getElementsByTagName('head')[0].appendChild(node);
5454 		}
5455 
5456 		/**
5457 		 * Loads the specified css style sheet file and call the loadedCallback once it's finished loading.
5458 		 *
5459 		 * @method load
5460 		 * @param {String} url Url to be loaded.
5461 		 * @param {Function} loadedCallback Callback to be executed when loaded.
5462 		 * @param {Function} errorCallback Callback to be executed when failed loading.
5463 		 */
5464 		function load(url, loadedCallback, errorCallback) {
5465 			var link, style, startTime, state;
5466 
5467 			function passed() {
5468 				var callbacks = state.passed, i = callbacks.length;
5469 
5470 				while (i--) {
5471 					callbacks[i]();
5472 				}
5473 
5474 				state.status = 2;
5475 				state.passed = [];
5476 				state.failed = [];
5477 			}
5478 
5479 			function failed() {
5480 				var callbacks = state.failed, i = callbacks.length;
5481 
5482 				while (i--) {
5483 					callbacks[i]();
5484 				}
5485 
5486 				state.status = 3;
5487 				state.passed = [];
5488 				state.failed = [];
5489 			}
5490 
5491 			// Sniffs for older WebKit versions that have the link.onload but a broken one
5492 			function isOldWebKit() {
5493 				var webKitChunks = navigator.userAgent.match(/WebKit\/(\d*)/);
5494 				return !!(webKitChunks && webKitChunks[1] < 536);
5495 			}
5496 
5497 			// Calls the waitCallback until the test returns true or the timeout occurs
5498 			function wait(testCallback, waitCallback) {
5499 				if (!testCallback()) {
5500 					// Wait for timeout
5501 					if ((new Date().getTime()) - startTime < maxLoadTime) {
5502 						window.setTimeout(waitCallback, 0);
5503 					} else {
5504 						failed();
5505 					}
5506 				}
5507 			}
5508 
5509 			// Workaround for WebKit that doesn't properly support the onload event for link elements
5510 			// Or WebKit that fires the onload event before the StyleSheet is added to the document
5511 			function waitForWebKitLinkLoaded() {
5512 				wait(function() {
5513 					var styleSheets = document.styleSheets, styleSheet, i = styleSheets.length, owner;
5514 
5515 					while (i--) {
5516 						styleSheet = styleSheets[i];
5517 						owner = styleSheet.ownerNode ? styleSheet.ownerNode : styleSheet.owningElement;
5518 						if (owner && owner.id === link.id) {
5519 							passed();
5520 							return true;
5521 						}
5522 					}
5523 				}, waitForWebKitLinkLoaded);
5524 			}
5525 
5526 			// Workaround for older Geckos that doesn't have any onload event for StyleSheets
5527 			function waitForGeckoLinkLoaded() {
5528 				wait(function() {
5529 					try {
5530 						// Accessing the cssRules will throw an exception until the CSS file is loaded
5531 						var cssRules = style.sheet.cssRules;
5532 						passed();
5533 						return !!cssRules;
5534 					} catch (ex) {
5535 						// Ignore
5536 					}
5537 				}, waitForGeckoLinkLoaded);
5538 			}
5539 
5540 			if (!loadedStates[url]) {
5541 				state = {
5542 					passed: [],
5543 					failed: []
5544 				};
5545 
5546 				loadedStates[url] = state;
5547 			} else {
5548 				state = loadedStates[url];
5549 			}
5550 
5551 			if (loadedCallback) {
5552 				state.passed.push(loadedCallback);
5553 			}
5554 
5555 			if (errorCallback) {
5556 				state.failed.push(errorCallback);
5557 			}
5558 
5559 			// Is loading wait for it to pass
5560 			if (state.status == 1) {
5561 				return;
5562 			}
5563 
5564 			// Has finished loading and was success
5565 			if (state.status == 2) {
5566 				passed();
5567 				return;
5568 			}
5569 
5570 			// Has finished loading and was a failure
5571 			if (state.status == 3) {
5572 				failed();
5573 				return;
5574 			}
5575 
5576 			// Start loading
5577 			state.status = 1;
5578 			link = document.createElement('link');
5579 			link.rel = 'stylesheet';
5580 			link.type = 'text/css';
5581 			link.id = 'u' + (idCount++);
5582 			link.async = false;
5583 			link.defer = false;
5584 			startTime = new Date().getTime();
5585 
5586 			// Feature detect onload on link element and sniff older webkits since it has an broken onload event
5587 			if ("onload" in link && !isOldWebKit()) {
5588 				link.onload = waitForWebKitLinkLoaded;
5589 				link.onerror = failed;
5590 			} else {
5591 				// Sniff for old Firefox that doesn't support the onload event on link elements
5592 				// TODO: Remove this in the future when everyone uses modern browsers
5593 				if (navigator.userAgent.indexOf("Firefox") > 0) {
5594 					style = document.createElement('style');
5595 					style.textContent = '@import "' + url + '"';
5596 					waitForGeckoLinkLoaded();
5597 					appendToHead(style);
5598 					return;
5599 				} else {
5600 					// Use the id owner on older webkits
5601 					waitForWebKitLinkLoaded();
5602 				}
5603 			}
5604 
5605 			appendToHead(link);
5606 			link.href = url;
5607 		}
5608 
5609 		this.load = load;
5610 	};
5611 });
5612 
5613 // Included from: js/tinymce/classes/dom/DOMUtils.js
5614 
5615 /**
5616  * DOMUtils.js
5617  *
5618  * Copyright, Moxiecode Systems AB
5619  * Released under LGPL License.
5620  *
5621  * License: http://www.tinymce.com/license
5622  * Contributing: http://www.tinymce.com/contributing
5623  */
5624 
5625 /**
5626  * Utility class for various DOM manipulation and retrieval functions.
5627  *
5628  * @class tinymce.dom.DOMUtils
5629  * @example
5630  * // Add a class to an element by id in the page
5631  * tinymce.DOM.addClass('someid', 'someclass');
5632  *
5633  * // Add a class to an element by id inside the editor
5634  * tinymce.activeEditor.dom.addClass('someid', 'someclass');
5635  */
5636 define("tinymce/dom/DOMUtils", [
5637 	"tinymce/dom/Sizzle",
5638 	"tinymce/html/Styles",
5639 	"tinymce/dom/EventUtils",
5640 	"tinymce/dom/TreeWalker",
5641 	"tinymce/dom/Range",
5642 	"tinymce/html/Entities",
5643 	"tinymce/Env",
5644 	"tinymce/util/Tools",
5645 	"tinymce/dom/StyleSheetLoader"
5646 ], function(Sizzle, Styles, EventUtils, TreeWalker, Range, Entities, Env, Tools, StyleSheetLoader) {
5647 	// Shorten names
5648 	var each = Tools.each, is = Tools.is, grep = Tools.grep, trim = Tools.trim, extend = Tools.extend;
5649 	var isWebKit = Env.webkit, isIE = Env.ie;
5650 	var simpleSelectorRe = /^([a-z0-9],?)+$/i;
5651 	var whiteSpaceRegExp = /^[ \t\r\n]*$/;
5652 	var numericCssMap = Tools.makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom', ' ');
5653 
5654 	/**
5655 	 * Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class.
5656 	 *
5657 	 * @constructor
5658 	 * @method DOMUtils
5659 	 * @param {Document} d Document reference to bind the utility class to.
5660 	 * @param {settings} s Optional settings collection.
5661 	 */
5662 	function DOMUtils(doc, settings) {
5663 		var self = this, blockElementsMap;
5664 
5665 		self.doc = doc;
5666 		self.win = window;
5667 		self.files = {};
5668 		self.counter = 0;
5669 		self.stdMode = !isIE || doc.documentMode >= 8;
5670 		self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode;
5671 		self.hasOuterHTML = "outerHTML" in doc.createElement("a");
5672 		self.styleSheetLoader = new StyleSheetLoader(doc);
5673 		this.boundEvents = [];
5674 
5675 		self.settings = settings = extend({
5676 			keep_values: false,
5677 			hex_colors: 1
5678 		}, settings);
5679 
5680 		self.schema = settings.schema;
5681 		self.styles = new Styles({
5682 			url_converter: settings.url_converter,
5683 			url_converter_scope: settings.url_converter_scope
5684 		}, settings.schema);
5685 
5686 		self.fixDoc(doc);
5687 		self.events = settings.ownEvents ? new EventUtils(settings.proxy) : EventUtils.Event;
5688 		blockElementsMap = settings.schema ? settings.schema.getBlockElements() : {};
5689 
5690 		/**
5691 		 * Returns true/false if the specified element is a block element or not.
5692 		 *
5693 		 * @method isBlock
5694 		 * @param {Node/String} node Element/Node to check.
5695 		 * @return {Boolean} True/False state if the node is a block element or not.
5696 		 */
5697 		self.isBlock = function(node) {
5698 			// Fix for #5446
5699 			if (!node) {
5700 				return false;
5701 			}
5702 
5703 			// This function is called in module pattern style since it might be executed with the wrong this scope
5704 			var type = node.nodeType;
5705 
5706 			// If it's a node then check the type and use the nodeName
5707 			if (type) {
5708 				return !!(type === 1 && blockElementsMap[node.nodeName]);
5709 			}
5710 
5711 			return !!blockElementsMap[node];
5712 		};
5713 	}
5714 
5715 	DOMUtils.prototype = {
5716 		root: null,
5717 		props: {
5718 			"for": "htmlFor",
5719 			"class": "className",
5720 			className: "className",
5721 			checked: "checked",
5722 			disabled: "disabled",
5723 			maxlength: "maxLength",
5724 			readonly: "readOnly",
5725 			selected: "selected",
5726 			value: "value",
5727 			id: "id",
5728 			name: "name",
5729 			type: "type"
5730 		},
5731 
5732 		fixDoc: function(doc) {
5733 			var settings = this.settings, name;
5734 
5735 			if (isIE && settings.schema) {
5736 				// Add missing HTML 4/5 elements to IE
5737 				('abbr article aside audio canvas ' +
5738 				'details figcaption figure footer ' +
5739 				'header hgroup mark menu meter nav ' +
5740 				'output progress section summary ' +
5741 				'time video').replace(/\w+/g, function(name) {
5742 					doc.createElement(name);
5743 				});
5744 
5745 				// Create all custom elements
5746 				for (name in settings.schema.getCustomElements()) {
5747 					doc.createElement(name);
5748 				}
5749 			}
5750 		},
5751 
5752 		clone: function(node, deep) {
5753 			var self = this, clone, doc;
5754 
5755 			// TODO: Add feature detection here in the future
5756 			if (!isIE || node.nodeType !== 1 || deep) {
5757 				return node.cloneNode(deep);
5758 			}
5759 
5760 			doc = self.doc;
5761 
5762 			// Make a HTML5 safe shallow copy
5763 			if (!deep) {
5764 				clone = doc.createElement(node.nodeName);
5765 
5766 				// Copy attribs
5767 				each(self.getAttribs(node), function(attr) {
5768 					self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));
5769 				});
5770 
5771 				return clone;
5772 			}
5773 /*
5774 			// Setup HTML5 patched document fragment
5775 			if (!self.frag) {
5776 				self.frag = doc.createDocumentFragment();
5777 				self.fixDoc(self.frag);
5778 			}
5779 
5780 			// Make a deep copy by adding it to the document fragment then removing it this removed the :section
5781 			clone = doc.createElement('div');
5782 			self.frag.appendChild(clone);
5783 			clone.innerHTML = node.outerHTML;
5784 			self.frag.removeChild(clone);
5785 */
5786 			return clone.firstChild;
5787 		},
5788 
5789 		/**
5790 		 * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not
5791 		 * go above the point of this root node.
5792 		 *
5793 		 * @method getRoot
5794 		 * @return {Element} Root element for the utility class.
5795 		 */
5796 		getRoot: function() {
5797 			var self = this;
5798 
5799 			return self.get(self.settings.root_element) || self.doc.body;
5800 		},
5801 
5802 		/**
5803 		 * Returns the viewport of the window.
5804 		 *
5805 		 * @method getViewPort
5806 		 * @param {Window} win Optional window to get viewport of.
5807 		 * @return {Object} Viewport object with fields x, y, w and h.
5808 		 */
5809 		getViewPort: function(win) {
5810 			var doc, rootElm;
5811 
5812 			win = !win ? this.win : win;
5813 			doc = win.document;
5814 			rootElm = this.boxModel ? doc.documentElement : doc.body;
5815 
5816 			// Returns viewport size excluding scrollbars
5817 			return {
5818 				x: win.pageXOffset || rootElm.scrollLeft,
5819 				y: win.pageYOffset || rootElm.scrollTop,
5820 				w: win.innerWidth || rootElm.clientWidth,
5821 				h: win.innerHeight || rootElm.clientHeight
5822 			};
5823 		},
5824 
5825 		/**
5826 		 * Returns the rectangle for a specific element.
5827 		 *
5828 		 * @method getRect
5829 		 * @param {Element/String} elm Element object or element ID to get rectangle from.
5830 		 * @return {object} Rectangle for specified element object with x, y, w, h fields.
5831 		 */
5832 		getRect: function(elm) {
5833 			var self = this, pos, size;
5834 
5835 			elm = self.get(elm);
5836 			pos = self.getPos(elm);
5837 			size = self.getSize(elm);
5838 
5839 			return {
5840 				x: pos.x, y: pos.y,
5841 				w: size.w, h: size.h
5842 			};
5843 		},
5844 
5845 		/**
5846 		 * Returns the size dimensions of the specified element.
5847 		 *
5848 		 * @method getSize
5849 		 * @param {Element/String} elm Element object or element ID to get rectangle from.
5850 		 * @return {object} Rectangle for specified element object with w, h fields.
5851 		 */
5852 		getSize: function(elm) {
5853 			var self = this, w, h;
5854 
5855 			elm = self.get(elm);
5856 			w = self.getStyle(elm, 'width');
5857 			h = self.getStyle(elm, 'height');
5858 
5859 			// Non pixel value, then force offset/clientWidth
5860 			if (w.indexOf('px') === -1) {
5861 				w = 0;
5862 			}
5863 
5864 			// Non pixel value, then force offset/clientWidth
5865 			if (h.indexOf('px') === -1) {
5866 				h = 0;
5867 			}
5868 
5869 			return {
5870 				w: parseInt(w, 10) || elm.offsetWidth || elm.clientWidth,
5871 				h: parseInt(h, 10) || elm.offsetHeight || elm.clientHeight
5872 			};
5873 		},
5874 
5875 		/**
5876 		 * Returns a node by the specified selector function. This function will
5877 		 * loop through all parent nodes and call the specified function for each node.
5878 		 * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end
5879 		 * and the node it found will be returned.
5880 		 *
5881 		 * @method getParent
5882 		 * @param {Node/String} node DOM node to search parents on or ID string.
5883 		 * @param {function} selector Selection function or CSS selector to execute on each node.
5884 		 * @param {Node} root Optional root element, never go below this point.
5885 		 * @return {Node} DOM Node or null if it wasn't found.
5886 		 */
5887 		getParent: function(node, selector, root) {
5888 			return this.getParents(node, selector, root, false);
5889 		},
5890 
5891 		/**
5892 		 * Returns a node list of all parents matching the specified selector function or pattern.
5893 		 * If the function then returns true indicating that it has found what it was looking for and that node will be collected.
5894 		 *
5895 		 * @method getParents
5896 		 * @param {Node/String} node DOM node to search parents on or ID string.
5897 		 * @param {function} selector Selection function to execute on each node or CSS pattern.
5898 		 * @param {Node} root Optional root element, never go below this point.
5899 		 * @return {Array} Array of nodes or null if it wasn't found.
5900 		 */
5901 		getParents: function(node, selector, root, collect) {
5902 			var self = this, selectorVal, result = [];
5903 
5904 			node = self.get(node);
5905 			collect = collect === undefined;
5906 
5907 			// Default root on inline mode
5908 			root = root || (self.getRoot().nodeName != 'BODY' ? self.getRoot().parentNode : null);
5909 
5910 			// Wrap node name as func
5911 			if (is(selector, 'string')) {
5912 				selectorVal = selector;
5913 
5914 				if (selector === '*') {
5915 					selector = function(node) {return node.nodeType == 1;};
5916 				} else {
5917 					selector = function(node) {
5918 						return self.is(node, selectorVal);
5919 					};
5920 				}
5921 			}
5922 
5923 			while (node) {
5924 				if (node == root || !node.nodeType || node.nodeType === 9) {
5925 					break;
5926 				}
5927 
5928 				if (!selector || selector(node)) {
5929 					if (collect) {
5930 						result.push(node);
5931 					} else {
5932 						return node;
5933 					}
5934 				}
5935 
5936 				node = node.parentNode;
5937 			}
5938 
5939 			return collect ? result : null;
5940 		},
5941 
5942 		/**
5943 		 * Returns the specified element by ID or the input element if it isn't a string.
5944 		 *
5945 		 * @method get
5946 		 * @param {String/Element} n Element id to look for or element to just pass though.
5947 		 * @return {Element} Element matching the specified id or null if it wasn't found.
5948 		 */
5949 		get: function(elm) {
5950 			var name;
5951 
5952 			if (elm && this.doc && typeof(elm) == 'string') {
5953 				name = elm;
5954 				elm = this.doc.getElementById(elm);
5955 
5956 				// IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
5957 				if (elm && elm.id !== name) {
5958 					return this.doc.getElementsByName(name)[1];
5959 				}
5960 			}
5961 
5962 			return elm;
5963 		},
5964 
5965 		/**
5966 		 * Returns the next node that matches selector or function
5967 		 *
5968 		 * @method getNext
5969 		 * @param {Node} node Node to find siblings from.
5970 		 * @param {String/function} selector Selector CSS expression or function.
5971 		 * @return {Node} Next node item matching the selector or null if it wasn't found.
5972 		 */
5973 		getNext: function(node, selector) {
5974 			return this._findSib(node, selector, 'nextSibling');
5975 		},
5976 
5977 		/**
5978 		 * Returns the previous node that matches selector or function
5979 		 *
5980 		 * @method getPrev
5981 		 * @param {Node} node Node to find siblings from.
5982 		 * @param {String/function} selector Selector CSS expression or function.
5983 		 * @return {Node} Previous node item matching the selector or null if it wasn't found.
5984 		 */
5985 		getPrev: function(node, selector) {
5986 			return this._findSib(node, selector, 'previousSibling');
5987 		},
5988 
5989 		// #ifndef jquery
5990 
5991 		/**
5992 		 * Selects specific elements by a CSS level 3 pattern. For example "div#a1 p.test".
5993 		 * This function is optimized for the most common patterns needed in TinyMCE but it also performs well enough
5994 		 * on more complex patterns.
5995 		 *
5996 		 * @method select
5997 		 * @param {String} selector CSS level 3 pattern to select/find elements by.
5998 		 * @param {Object} scope Optional root element/scope element to search in.
5999 		 * @return {Array} Array with all matched elements.
6000 		 * @example
6001 		 * // Adds a class to all paragraphs in the currently active editor
6002 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
6003 		 *
6004 		 * // Adds a class to all spans that have the test class in the currently active editor
6005 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass')
6006 		 */
6007 		select: function(selector, scope) {
6008 			var self = this;
6009 
6010 			//Sizzle.selectors.cacheLength = 0;
6011 			return Sizzle(selector, self.get(scope) || self.get(self.settings.root_element) || self.doc, []);
6012 		},
6013 
6014 		/**
6015 		 * Returns true/false if the specified element matches the specified css pattern.
6016 		 *
6017 		 * @method is
6018 		 * @param {Node/NodeList} elm DOM node to match or an array of nodes to match.
6019 		 * @param {String} selector CSS pattern to match the element against.
6020 		 */
6021 		is: function(elm, selector) {
6022 			var i;
6023 
6024 			// If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance
6025 			if (elm.length === undefined) {
6026 				// Simple all selector
6027 				if (selector === '*') {
6028 					return elm.nodeType == 1;
6029 				}
6030 
6031 				// Simple selector just elements
6032 				if (simpleSelectorRe.test(selector)) {
6033 					selector = selector.toLowerCase().split(/,/);
6034 					elm = elm.nodeName.toLowerCase();
6035 
6036 					for (i = selector.length - 1; i >= 0; i--) {
6037 						if (selector[i] == elm) {
6038 							return true;
6039 						}
6040 					}
6041 
6042 					return false;
6043 				}
6044 			}
6045 
6046 			// Is non element
6047 			if (elm.nodeType && elm.nodeType != 1) {
6048 				return false;
6049 			}
6050 
6051 			var elms = elm.nodeType ? [elm] : elm;
6052 			return Sizzle(selector, elms[0].ownerDocument || elms[0], null, elms).length > 0;
6053 		},
6054 
6055 		// #endif
6056 
6057 		/**
6058 		 * Adds the specified element to another element or elements.
6059 		 *
6060 		 * @method add
6061 		 * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to.
6062 		 * @param {String/Element} name Name of new element to add or existing element to add.
6063 		 * @param {Object} attrs Optional object collection with arguments to add to the new element(s).
6064 		 * @param {String} html Optional inner HTML contents to add for each element.
6065 		 * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements
6066 		 * were passed in.
6067 		 * @example
6068 		 * // Adds a new paragraph to the end of the active editor
6069 		 * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', {title: 'my title'}, 'Some content');
6070 		 */
6071 		add: function(parentElm, name, attrs, html, create) {
6072 			var self = this;
6073 
6074 			return this.run(parentElm, function(parentElm) {
6075 				var newElm;
6076 
6077 				newElm = is(name, 'string') ? self.doc.createElement(name) : name;
6078 				self.setAttribs(newElm, attrs);
6079 
6080 				if (html) {
6081 					if (html.nodeType) {
6082 						newElm.appendChild(html);
6083 					} else {
6084 						self.setHTML(newElm, html);
6085 					}
6086 				}
6087 
6088 				return !create ? parentElm.appendChild(newElm) : newElm;
6089 			});
6090 		},
6091 
6092 		/**
6093 		 * Creates a new element.
6094 		 *
6095 		 * @method create
6096 		 * @param {String} name Name of new element.
6097 		 * @param {Object} attrs Optional object name/value collection with element attributes.
6098 		 * @param {String} html Optional HTML string to set as inner HTML of the element.
6099 		 * @return {Element} HTML DOM node element that got created.
6100 		 * @example
6101 		 * // Adds an element where the caret/selection is in the active editor
6102 		 * var el = tinymce.activeEditor.dom.create('div', {id: 'test', 'class': 'myclass'}, 'some content');
6103 		 * tinymce.activeEditor.selection.setNode(el);
6104 		 */
6105 		create: function(name, attrs, html) {
6106 			return this.add(this.doc.createElement(name), name, attrs, html, 1);
6107 		},
6108 
6109 		/**
6110 		 * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in.
6111 		 *
6112 		 * @method createHTML
6113 		 * @param {String} name Name of new element.
6114 		 * @param {Object} attrs Optional object name/value collection with element attributes.
6115 		 * @param {String} html Optional HTML string to set as inner HTML of the element.
6116 		 * @return {String} String with new HTML element, for example: <a href="#">test</a>.
6117 		 * @example
6118 		 * // Creates a html chunk and inserts it at the current selection/caret location
6119 		 * tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', {href: 'test.html'}, 'some line'));
6120 		 */
6121 		createHTML: function(name, attrs, html) {
6122 			var outHtml = '', key;
6123 
6124 			outHtml += '<' + name;
6125 
6126 			for (key in attrs) {
6127 				if (attrs.hasOwnProperty(key) && attrs[key] !== null) {
6128 					outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"';
6129 				}
6130 			}
6131 
6132 			// A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime
6133 			if (typeof(html) != "undefined") {
6134 				return outHtml + '>' + html + '</' + name + '>';
6135 			}
6136 
6137 			return outHtml + ' />';
6138 		},
6139 
6140 		/**
6141 		 * Creates a document fragment out of the specified HTML string.
6142 		 *
6143 		 * @method createFragment
6144 		 * @param {String} html Html string to create fragment from.
6145 		 * @return {DocumentFragment} Document fragment node.
6146 		 */
6147 		createFragment: function(html) {
6148 			var frag, node, doc = this.doc, container;
6149 
6150 			container = doc.createElement("div");
6151 			frag = doc.createDocumentFragment();
6152 
6153 			if (html) {
6154 				container.innerHTML = html;
6155 			}
6156 
6157 			while ((node = container.firstChild)) {
6158 				frag.appendChild(node);
6159 			}
6160 
6161 			return frag;
6162 		},
6163 
6164 		/**
6165 		 * Removes/deletes the specified element(s) from the DOM.
6166 		 *
6167 		 * @method remove
6168 		 * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids.
6169 		 * @param {Boolean} keep_children Optional state to keep children or not. If set to true all children will be
6170 		 * placed at the location of the removed element.
6171 		 * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements
6172 		 * were passed in.
6173 		 * @example
6174 		 * // Removes all paragraphs in the active editor
6175 		 * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p'));
6176 		 *
6177 		 * // Removes an element by id in the document
6178 		 * tinymce.DOM.remove('mydiv');
6179 		 */
6180 		remove: function(node, keep_children) {
6181 			return this.run(node, function(node) {
6182 				var child, parent = node.parentNode;
6183 
6184 				if (!parent) {
6185 					return null;
6186 				}
6187 
6188 				if (keep_children) {
6189 					while ((child = node.firstChild)) {
6190 						// IE 8 will crash if you don't remove completely empty text nodes
6191 						if (!isIE || child.nodeType !== 3 || child.nodeValue) {
6192 							parent.insertBefore(child, node);
6193 						} else {
6194 							node.removeChild(child);
6195 						}
6196 					}
6197 				}
6198 
6199 				return parent.removeChild(node);
6200 			});
6201 		},
6202 
6203 		/**
6204 		 * Sets the CSS style value on a HTML element. The name can be a camelcase string
6205 		 * or the CSS style name like background-color.
6206 		 *
6207 		 * @method setStyle
6208 		 * @param {String/Element/Array} n HTML element/Element ID or Array of elements/ids to set CSS style value on.
6209 		 * @param {String} na Name of the style value to set.
6210 		 * @param {String} v Value to set on the style.
6211 		 * @example
6212 		 * // Sets a style value on all paragraphs in the currently active editor
6213 		 * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red');
6214 		 *
6215 		 * // Sets a style value to an element by id in the current document
6216 		 * tinymce.DOM.setStyle('mydiv', 'background-color', 'red');
6217 		 */
6218 		setStyle: function(elm, name, value) {
6219 			return this.run(elm, function(elm) {
6220 				var self = this, style, key;
6221 
6222 				if (name) {
6223 					if (typeof(name) === 'string') {
6224 						style = elm.style;
6225 
6226 						// Camelcase it, if needed
6227 						name = name.replace(/-(\D)/g, function(a, b) {
6228 							return b.toUpperCase();
6229 						});
6230 
6231 						// Default px suffix on these
6232 						if (typeof(value) === 'number' && !numericCssMap[name]) {
6233 							value += 'px';
6234 						}
6235 
6236 						// IE specific opacity
6237 						if (name === "opacity" && elm.runtimeStyle && typeof(elm.runtimeStyle.opacity) === "undefined") {
6238 							style.filter = value === '' ? '' : "alpha(opacity=" + (value * 100) + ")";
6239 						}
6240 
6241 						if (name == "float") {
6242 							// Old IE vs modern browsers
6243 							name = "cssFloat" in elm.style ? "cssFloat" : "styleFloat";
6244 						}
6245 
6246 						try {
6247 							style[name] = value;
6248 						} catch (ex) {
6249 							// Ignore IE errors
6250 						}
6251 
6252 						// Force update of the style data
6253 						if (self.settings.update_styles) {
6254 							elm.removeAttribute('data-mce-style');
6255 						}
6256 					} else {
6257 						for (key in name) {
6258 							self.setStyle(elm, key, name[key]);
6259 						}
6260 					}
6261 				}
6262 			});
6263 		},
6264 
6265 		/**
6266 		 * Returns the current style or runtime/computed value of an element.
6267 		 *
6268 		 * @method getStyle
6269 		 * @param {String/Element} elm HTML element or element id string to get style from.
6270 		 * @param {String} name Style name to return.
6271 		 * @param {Boolean} computed Computed style.
6272 		 * @return {String} Current style or computed style value of an element.
6273 		 */
6274 		getStyle: function(elm, name, computed) {
6275 			elm = this.get(elm);
6276 
6277 			if (!elm) {
6278 				return;
6279 			}
6280 
6281 			// W3C
6282 			if (this.doc.defaultView && computed) {
6283 				// Remove camelcase
6284 				name = name.replace(/[A-Z]/g, function(a){
6285 					return '-' + a;
6286 				});
6287 
6288 				try {
6289 					return this.doc.defaultView.getComputedStyle(elm, null).getPropertyValue(name);
6290 				} catch (ex) {
6291 					// Old safari might fail
6292 					return null;
6293 				}
6294 			}
6295 
6296 			// Camelcase it, if needed
6297 			name = name.replace(/-(\D)/g, function(a, b) {
6298 				return b.toUpperCase();
6299 			});
6300 
6301 			if (name == 'float') {
6302 				name = isIE ? 'styleFloat' : 'cssFloat';
6303 			}
6304 
6305 			// IE & Opera
6306 			if (elm.currentStyle && computed) {
6307 				return elm.currentStyle[name];
6308 			}
6309 
6310 			return elm.style ? elm.style[name] : undefined;
6311 		},
6312 
6313 		/**
6314 		 * Sets multiple styles on the specified element(s).
6315 		 *
6316 		 * @method setStyles
6317 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set styles on.
6318 		 * @param {Object} o Name/Value collection of style items to add to the element(s).
6319 		 * @example
6320 		 * // Sets styles on all paragraphs in the currently active editor
6321 		 * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), {'background-color': 'red', 'color': 'green'});
6322 		 *
6323 		 * // Sets styles to an element by id in the current document
6324 		 * tinymce.DOM.setStyles('mydiv', {'background-color': 'red', 'color': 'green'});
6325 		 */
6326 		setStyles: function(elm, styles) {
6327 			this.setStyle(elm, styles);
6328 		},
6329 
6330 		css: function(elm, name, value) {
6331 			this.setStyle(elm, name, value);
6332 		},
6333 
6334 		/**
6335 		 * Removes all attributes from an element or elements.
6336 		 *
6337 		 * @method removeAllAttribs
6338 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from.
6339 		 */
6340 		removeAllAttribs: function(e) {
6341 			return this.run(e, function(e) {
6342 				var i, attrs = e.attributes;
6343 				for (i = attrs.length - 1; i >= 0; i--) {
6344 					e.removeAttributeNode(attrs.item(i));
6345 				}
6346 			});
6347 		},
6348 
6349 		/**
6350 		 * Sets the specified attribute of an element or elements.
6351 		 *
6352 		 * @method setAttrib
6353 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set attribute on.
6354 		 * @param {String} n Name of attribute to set.
6355 		 * @param {String} v Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove the attribute instead.
6356 		 * @example
6357 		 * // Sets class attribute on all paragraphs in the active editor
6358 		 * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass');
6359 		 *
6360 		 * // Sets class attribute on a specific element in the current page
6361 		 * tinymce.dom.setAttrib('mydiv', 'class', 'myclass');
6362 		 */
6363 		setAttrib: function(e, n, v) {
6364 			var self = this;
6365 
6366 			// What's the point
6367 			if (!e || !n) {
6368 				return;
6369 			}
6370 
6371 			return this.run(e, function(e) {
6372 				var s = self.settings;
6373 				var originalValue = e.getAttribute(n);
6374 				if (v !== null) {
6375 					switch (n) {
6376 						case "style":
6377 							if (!is(v, 'string')) {
6378 								each(v, function(v, n) {
6379 									self.setStyle(e, n, v);
6380 								});
6381 
6382 								return;
6383 							}
6384 
6385 							// No mce_style for elements with these since they might get resized by the user
6386 							if (s.keep_values) {
6387 								if (v) {
6388 									e.setAttribute('data-mce-style', v, 2);
6389 								} else {
6390 									e.removeAttribute('data-mce-style', 2);
6391 								}
6392 							}
6393 
6394 							e.style.cssText = v;
6395 							break;
6396 
6397 						case "class":
6398 							e.className = v || ''; // Fix IE null bug
6399 							break;
6400 
6401 						case "src":
6402 						case "href":
6403 							if (s.keep_values) {
6404 								if (s.url_converter) {
6405 									v = s.url_converter.call(s.url_converter_scope || self, v, n, e);
6406 								}
6407 
6408 								self.setAttrib(e, 'data-mce-' + n, v, 2);
6409 							}
6410 
6411 							break;
6412 
6413 						case "shape":
6414 							e.setAttribute('data-mce-style', v);
6415 							break;
6416 					}
6417 				}
6418 				if (is(v) && v !== null && v.length !== 0) {
6419 					e.setAttribute(n, '' + v, 2);
6420 				} else {
6421 					e.removeAttribute(n, 2);
6422 				}
6423 
6424 				// fire onChangeAttrib event for attributes that have changed
6425 				if (originalValue != v && s.onSetAttrib) {
6426 					s.onSetAttrib({attrElm: e, attrName: n, attrValue: v});
6427 				}
6428 			});
6429 		},
6430 
6431 		/**
6432 		 * Sets two or more specified attributes of an element or elements.
6433 		 *
6434 		 * @method setAttribs
6435 		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on.
6436 		 * @param {Object} attrs Name/Value collection of attribute items to add to the element(s).
6437 		 * @example
6438 		 * // Sets class and title attributes on all paragraphs in the active editor
6439 		 * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), {'class': 'myclass', title: 'some title'});
6440 		 *
6441 		 * // Sets class and title attributes on a specific element in the current page
6442 		 * tinymce.DOM.setAttribs('mydiv', {'class': 'myclass', title: 'some title'});
6443 		 */
6444 		setAttribs: function(elm, attrs) {
6445 			var self = this;
6446 
6447 			return this.run(elm, function(elm) {
6448 				each(attrs, function(value, name) {
6449 					self.setAttrib(elm, name, value);
6450 				});
6451 			});
6452 		},
6453 
6454 		/**
6455 		 * Returns the specified attribute by name.
6456 		 *
6457 		 * @method getAttrib
6458 		 * @param {String/Element} elm Element string id or DOM element to get attribute from.
6459 		 * @param {String} name Name of attribute to get.
6460 		 * @param {String} defaultVal Optional default value to return if the attribute didn't exist.
6461 		 * @return {String} Attribute value string, default value or null if the attribute wasn't found.
6462 		 */
6463 		getAttrib: function(elm, name, defaultVal) {
6464 			var value, self = this, undef;
6465 
6466 			elm = self.get(elm);
6467 
6468 			if (!elm || elm.nodeType !== 1) {
6469 				return defaultVal === undef ? false : defaultVal;
6470 			}
6471 
6472 			if (!is(defaultVal)) {
6473 				defaultVal = '';
6474 			}
6475 
6476 			// Try the mce variant for these
6477 			if (/^(src|href|style|coords|shape)$/.test(name)) {
6478 				value = elm.getAttribute("data-mce-" + name);
6479 
6480 				if (value) {
6481 					return value;
6482 				}
6483 			}
6484 
6485 			if (isIE && self.props[name]) {
6486 				value = elm[self.props[name]];
6487 				value = value && value.nodeValue ? value.nodeValue : value;
6488 			}
6489 
6490 			if (!value) {
6491 				value = elm.getAttribute(name, 2);
6492 			}
6493 
6494 			// Check boolean attribs
6495 			if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(name)) {
6496 				if (elm[self.props[name]] === true && value === '') {
6497 					return name;
6498 				}
6499 
6500 				return value ? name : '';
6501 			}
6502 
6503 			// Inner input elements will override attributes on form elements
6504 			if (elm.nodeName === "FORM" && elm.getAttributeNode(name)) {
6505 				return elm.getAttributeNode(name).nodeValue;
6506 			}
6507 
6508 			if (name === 'style') {
6509 				value = value || elm.style.cssText;
6510 
6511 				if (value) {
6512 					value = self.serializeStyle(self.parseStyle(value), elm.nodeName);
6513 
6514 					if (self.settings.keep_values) {
6515 						elm.setAttribute('data-mce-style', value);
6516 					}
6517 				}
6518 			}
6519 
6520 			// Remove Apple and WebKit stuff
6521 			if (isWebKit && name === "class" && value) {
6522 				value = value.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
6523 			}
6524 
6525 			// Handle IE issues
6526 			if (isIE) {
6527 				switch (name) {
6528 					case 'rowspan':
6529 					case 'colspan':
6530 						// IE returns 1 as default value
6531 						if (value === 1) {
6532 							value = '';
6533 						}
6534 
6535 						break;
6536 
6537 					case 'size':
6538 						// IE returns +0 as default value for size
6539 						if (value === '+0' || value === 20 || value === 0) {
6540 							value = '';
6541 						}
6542 
6543 						break;
6544 
6545 					case 'width':
6546 					case 'height':
6547 					case 'vspace':
6548 					case 'checked':
6549 					case 'disabled':
6550 					case 'readonly':
6551 						if (value === 0) {
6552 							value = '';
6553 						}
6554 
6555 						break;
6556 
6557 					case 'hspace':
6558 						// IE returns -1 as default value
6559 						if (value === -1) {
6560 							value = '';
6561 						}
6562 
6563 						break;
6564 
6565 					case 'maxlength':
6566 					case 'tabindex':
6567 						// IE returns default value
6568 						if (value === 32768 || value === 2147483647 || value === '32768') {
6569 							value = '';
6570 						}
6571 
6572 						break;
6573 
6574 					case 'multiple':
6575 					case 'compact':
6576 					case 'noshade':
6577 					case 'nowrap':
6578 						if (value === 65535) {
6579 							return name;
6580 						}
6581 
6582 						return defaultVal;
6583 
6584 					case 'shape':
6585 						value = value.toLowerCase();
6586 						break;
6587 
6588 					default:
6589 						// IE has odd anonymous function for event attributes
6590 						if (name.indexOf('on') === 0 && value) {
6591 							value = ('' + value).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');
6592 						}
6593 				}
6594 			}
6595 
6596 			return (value !== undef && value !== null && value !== '') ? '' + value : defaultVal;
6597 		},
6598 
6599 		/**
6600 		 * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields.
6601 		 *
6602 		 * @method getPos
6603 		 * @param {Element/String} elm HTML element or element id to get x, y position from.
6604 		 * @param {Element} rootElm Optional root element to stop calculations at.
6605 		 * @return {object} Absolute position of the specified element object with x, y fields.
6606 		 */
6607 		getPos: function(elm, rootElm) {
6608 			var self = this, x = 0, y = 0, offsetParent, doc = self.doc, pos;
6609 
6610 			elm = self.get(elm);
6611 			rootElm = rootElm || doc.body;
6612 
6613 			if (elm) {
6614 				// Use getBoundingClientRect if it exists since it's faster than looping offset nodes
6615 				if (rootElm === doc.body && elm.getBoundingClientRect) {
6616 					pos = elm.getBoundingClientRect();
6617 					rootElm = self.boxModel ? doc.documentElement : doc.body;
6618 
6619 					// Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
6620 					// Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
6621 					x = pos.left + (doc.documentElement.scrollLeft || doc.body.scrollLeft) - rootElm.clientLeft;
6622 					y = pos.top + (doc.documentElement.scrollTop || doc.body.scrollTop) - rootElm.clientTop;
6623 
6624 					return {x: x, y: y};
6625 				}
6626 
6627 				offsetParent = elm;
6628 				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
6629 					x += offsetParent.offsetLeft || 0;
6630 					y += offsetParent.offsetTop || 0;
6631 					offsetParent = offsetParent.offsetParent;
6632 				}
6633 
6634 				offsetParent = elm.parentNode;
6635 				while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
6636 					x -= offsetParent.scrollLeft || 0;
6637 					y -= offsetParent.scrollTop || 0;
6638 					offsetParent = offsetParent.parentNode;
6639 				}
6640 			}
6641 
6642 			return {x: x, y: y};
6643 		},
6644 
6645 		/**
6646 		 * Parses the specified style value into an object collection. This parser will also
6647 		 * merge and remove any redundant items that browsers might have added. It will also convert non-hex
6648 		 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
6649 		 *
6650 		 * @method parseStyle
6651 		 * @param {String} cssText Style value to parse, for example: border:1px solid red;.
6652 		 * @return {Object} Object representation of that style, for example: {border: '1px solid red'}
6653 		 */
6654 		parseStyle: function(cssText) {
6655 			return this.styles.parse(cssText);
6656 		},
6657 
6658 		/**
6659 		 * Serializes the specified style object into a string.
6660 		 *
6661 		 * @method serializeStyle
6662 		 * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'}
6663 		 * @param {String} name Optional element name.
6664 		 * @return {String} String representation of the style object, for example: border: 1px solid red.
6665 		 */
6666 		serializeStyle: function(styles, name) {
6667 			return this.styles.serialize(styles, name);
6668 		},
6669 
6670 		/**
6671 		 * Adds a style element at the top of the document with the specified cssText content.
6672 		 *
6673 		 * @method addStyle
6674 		 * @param {String} cssText CSS Text style to add to top of head of document.
6675 		 */
6676 		addStyle: function(cssText) {
6677 			var self = this, doc = self.doc, head, styleElm;
6678 
6679 			// Prevent inline from loading the same styles twice
6680 			if (self !== DOMUtils.DOM && doc === document) {
6681 				var addedStyles = DOMUtils.DOM.addedStyles;
6682 
6683 				addedStyles = addedStyles || [];
6684 				if (addedStyles[cssText]) {
6685 					return;
6686 				}
6687 
6688 				addedStyles[cssText] = true;
6689 				DOMUtils.DOM.addedStyles = addedStyles;
6690 			}
6691 
6692 			// Create style element if needed
6693 			styleElm = doc.getElementById('mceDefaultStyles');
6694 			if (!styleElm) {
6695 				styleElm = doc.createElement('style');
6696 				styleElm.id = 'mceDefaultStyles';
6697 				styleElm.type = 'text/css';
6698 
6699 				head = doc.getElementsByTagName('head')[0];
6700 				if (head.firstChild) {
6701 					head.insertBefore(styleElm, head.firstChild);
6702 				} else {
6703 					head.appendChild(styleElm);
6704 				}
6705 			}
6706 
6707 			// Append style data to old or new style element
6708 			if (styleElm.styleSheet) {
6709 				styleElm.styleSheet.cssText += cssText;
6710 			} else {
6711 				styleElm.appendChild(doc.createTextNode(cssText));
6712 			}
6713 		},
6714 
6715 		/**
6716 		 * Imports/loads the specified CSS file into the document bound to the class.
6717 		 *
6718 		 * @method loadCSS
6719 		 * @param {String} u URL to CSS file to load.
6720 		 * @example
6721 		 * // Loads a CSS file dynamically into the current document
6722 		 * tinymce.DOM.loadCSS('somepath/some.css');
6723 		 *
6724 		 * // Loads a CSS file into the currently active editor instance
6725 		 * tinymce.activeEditor.dom.loadCSS('somepath/some.css');
6726 		 *
6727 		 * // Loads a CSS file into an editor instance by id
6728 		 * tinymce.get('someid').dom.loadCSS('somepath/some.css');
6729 		 *
6730 		 * // Loads multiple CSS files into the current document
6731 		 * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css');
6732 		 */
6733 		loadCSS: function(url) {
6734 			var self = this, doc = self.doc, head;
6735 
6736 			// Prevent inline from loading the same CSS file twice
6737 			if (self !== DOMUtils.DOM && doc === document) {
6738 				DOMUtils.DOM.loadCSS(url);
6739 				return;
6740 			}
6741 
6742 			if (!url) {
6743 				url = '';
6744 			}
6745 
6746 			head = doc.getElementsByTagName('head')[0];
6747 
6748 			each(url.split(','), function(url) {
6749 				var link;
6750 
6751 				if (self.files[url]) {
6752 					return;
6753 				}
6754 
6755 				self.files[url] = true;
6756 				link = self.create('link', {rel: 'stylesheet', href: url});
6757 
6758 				// IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
6759 				// This fix seems to resolve that issue by recalcing the document once a stylesheet finishes loading
6760 				// It's ugly but it seems to work fine.
6761 				if (isIE && doc.documentMode && doc.recalc) {
6762 					link.onload = function() {
6763 						if (doc.recalc) {
6764 							doc.recalc();
6765 						}
6766 
6767 						link.onload = null;
6768 					};
6769 				}
6770 
6771 				head.appendChild(link);
6772 			});
6773 		},
6774 
6775 		/**
6776 		 * Adds a class to the specified element or elements.
6777 		 *
6778 		 * @method addClass
6779 		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
6780 		 * @param {String} cls Class name to add to each element.
6781 		 * @return {String/Array} String with new class value or array with new class values for all elements.
6782 		 * @example
6783 		 * // Adds a class to all paragraphs in the active editor
6784 		 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass');
6785 		 *
6786 		 * // Adds a class to a specific element in the current page
6787 		 * tinymce.DOM.addClass('mydiv', 'myclass');
6788 		 */
6789 		addClass: function(elm, cls) {
6790 			return this.run(elm, function(elm) {
6791 				var clsVal;
6792 
6793 				if (!cls) {
6794 					return 0;
6795 				}
6796 
6797 				if (this.hasClass(elm, cls)) {
6798 					return elm.className;
6799 				}
6800 
6801 				clsVal = this.removeClass(elm, cls);
6802 				elm.className = clsVal = (clsVal !== '' ? (clsVal + ' ') : '') + cls;
6803 
6804 				return clsVal;
6805 			});
6806 		},
6807 
6808 		/**
6809 		 * Removes a class from the specified element or elements.
6810 		 *
6811 		 * @method removeClass
6812 		 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
6813 		 * @param {String} cls Class name to remove from each element.
6814 		 * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements
6815 		 * were passed in.
6816 		 * @example
6817 		 * // Removes a class from all paragraphs in the active editor
6818 		 * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass');
6819 		 *
6820 		 * // Removes a class from a specific element in the current page
6821 		 * tinymce.DOM.removeClass('mydiv', 'myclass');
6822 		 */
6823 		removeClass: function(elm, cls) {
6824 			var self = this, re;
6825 
6826 			return self.run(elm, function(elm) {
6827 				var val;
6828 
6829 				if (self.hasClass(elm, cls)) {
6830 					if (!re) {
6831 						re = new RegExp("(^|\\s+)" + cls + "(\\s+|$)", "g");
6832 					}
6833 
6834 					val = elm.className.replace(re, ' ');
6835 					val = trim(val != ' ' ? val : '');
6836 
6837 					elm.className = val;
6838 
6839 					// Empty class attr
6840 					if (!val) {
6841 						elm.removeAttribute('class');
6842 						elm.removeAttribute('className');
6843 					}
6844 
6845 					return val;
6846 				}
6847 
6848 				return elm.className;
6849 			});
6850 		},
6851 
6852 		/**
6853 		 * Returns true if the specified element has the specified class.
6854 		 *
6855 		 * @method hasClass
6856 		 * @param {String/Element} n HTML element or element id string to check CSS class on.
6857 		 * @param {String} c CSS class to check for.
6858 		 * @return {Boolean} true/false if the specified element has the specified class.
6859 		 */
6860 		hasClass: function(elm, cls) {
6861 			elm = this.get(elm);
6862 
6863 			if (!elm || !cls) {
6864 				return false;
6865 			}
6866 
6867 			return (' ' + elm.className + ' ').indexOf(' ' + cls + ' ') !== -1;
6868 		},
6869 
6870 		/**
6871 		 * Toggles the specified class on/off.
6872 		 *
6873 		 * @method toggleClass
6874 		 * @param {Element} elm Element to toggle class on.
6875 		 * @param {[type]} cls Class to toggle on/off.
6876 		 * @param {[type]} state Optional state to set.
6877 		 */
6878 		toggleClass: function(elm, cls, state) {
6879 			state = state === undefined ? !this.hasClass(elm, cls) : state;
6880 
6881 			if (this.hasClass(elm, cls) !== state) {
6882 				if (state) {
6883 					this.addClass(elm, cls);
6884 				} else {
6885 					this.removeClass(elm, cls);
6886 				}
6887 			}
6888 		},
6889 
6890 		/**
6891 		 * Shows the specified element(s) by ID by setting the "display" style.
6892 		 *
6893 		 * @method show
6894 		 * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show.
6895 		 */
6896 		show: function(elm) {
6897 			return this.setStyle(elm, 'display', 'block');
6898 		},
6899 
6900 		/**
6901 		 * Hides the specified element(s) by ID by setting the "display" style.
6902 		 *
6903 		 * @method hide
6904 		 * @param {String/Element/Array} e ID of DOM element or DOM element or array with elements or IDs to hide.
6905 		 * @example
6906 		 * // Hides an element by id in the document
6907 		 * tinymce.DOM.hide('myid');
6908 		 */
6909 		hide: function(elm) {
6910 			return this.setStyle(elm, 'display', 'none');
6911 		},
6912 
6913 		/**
6914 		 * Returns true/false if the element is hidden or not by checking the "display" style.
6915 		 *
6916 		 * @method isHidden
6917 		 * @param {String/Element} e Id or element to check display state on.
6918 		 * @return {Boolean} true/false if the element is hidden or not.
6919 		 */
6920 		isHidden: function(elm) {
6921 			elm = this.get(elm);
6922 
6923 			return !elm || elm.style.display == 'none' || this.getStyle(elm, 'display') == 'none';
6924 		},
6925 
6926 		/**
6927 		 * Returns a unique id. This can be useful when generating elements on the fly.
6928 		 * This method will not check if the element already exists.
6929 		 *
6930 		 * @method uniqueId
6931 		 * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_".
6932 		 * @return {String} Unique id.
6933 		 */
6934 		uniqueId: function(prefix) {
6935 			return (!prefix ? 'mce_' : prefix) + (this.counter++);
6936 		},
6937 
6938 		/**
6939 		 * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means
6940 		 * URLs will get converted, hex color values fixed etc. Check processHTML for details.
6941 		 *
6942 		 * @method setHTML
6943 		 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set HTML inside of.
6944 		 * @param {String} h HTML content to set as inner HTML of the element.
6945 		 * @example
6946 		 * // Sets the inner HTML of all paragraphs in the active editor
6947 		 * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html');
6948 		 *
6949 		 * // Sets the inner HTML of an element by id in the document
6950 		 * tinymce.DOM.setHTML('mydiv', 'some inner html');
6951 		 */
6952 		setHTML: function(element, html) {
6953 			var self = this;
6954 
6955 			return self.run(element, function(element) {
6956 				if (isIE) {
6957 					// Remove all child nodes, IE keeps empty text nodes in DOM
6958 					while (element.firstChild) {
6959 						element.removeChild(element.firstChild);
6960 					}
6961 
6962 					try {
6963 						// IE will remove comments from the beginning
6964 						// unless you padd the contents with something
6965 						element.innerHTML = '<br />' + html;
6966 						element.removeChild(element.firstChild);
6967 					} catch (ex) {
6968 						// IE sometimes produces an unknown runtime error on innerHTML if it's a block element
6969 						// within a block element for example a div inside a p
6970 						// This seems to fix this problem
6971 
6972 						// Create new div with HTML contents and a BR in front to keep comments
6973 						var newElement = self.create('div');
6974 						newElement.innerHTML = '<br />' + html;
6975 
6976 						// Add all children from div to target
6977 						each(grep(newElement.childNodes), function(node, i) {
6978 							// Skip br element
6979 							if (i && element.canHaveHTML) {
6980 								element.appendChild(node);
6981 							}
6982 						});
6983 					}
6984 				} else {
6985 					element.innerHTML = html;
6986 				}
6987 
6988 				return html;
6989 			});
6990 		},
6991 
6992 		/**
6993 		 * Returns the outer HTML of an element.
6994 		 *
6995 		 * @method getOuterHTML
6996 		 * @param {String/Element} elm Element ID or element object to get outer HTML from.
6997 		 * @return {String} Outer HTML string.
6998 		 * @example
6999 		 * tinymce.DOM.getOuterHTML(editorElement);
7000 		 * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody());
7001 		 */
7002 		getOuterHTML: function(elm) {
7003 			var doc, self = this;
7004 
7005 			elm = self.get(elm);
7006 
7007 			if (!elm) {
7008 				return null;
7009 			}
7010 
7011 			if (elm.nodeType === 1 && self.hasOuterHTML) {
7012 				return elm.outerHTML;
7013 			}
7014 
7015 			doc = (elm.ownerDocument || self.doc).createElement("body");
7016 			doc.appendChild(elm.cloneNode(true));
7017 
7018 			return doc.innerHTML;
7019 		},
7020 
7021 		/**
7022 		 * Sets the specified outer HTML on an element or elements.
7023 		 *
7024 		 * @method setOuterHTML
7025 		 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on.
7026 		 * @param {Object} html HTML code to set as outer value for the element.
7027 		 * @param {Document} doc Optional document scope to use in this process - defaults to the document of the DOM class.
7028 		 * @example
7029 		 * // Sets the outer HTML of all paragraphs in the active editor
7030 		 * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '<div>some html</div>');
7031 		 *
7032 		 * // Sets the outer HTML of an element by id in the document
7033 		 * tinymce.DOM.setOuterHTML('mydiv', '<div>some html</div>');
7034 		 */
7035 		setOuterHTML: function(elm, html, doc) {
7036 			var self = this;
7037 
7038 			return self.run(elm, function(elm) {
7039 				function set() {
7040 					var node, tempElm;
7041 
7042 					tempElm = doc.createElement("body");
7043 					tempElm.innerHTML = html;
7044 
7045 					node = tempElm.lastChild;
7046 					while (node) {
7047 						self.insertAfter(node.cloneNode(true), elm);
7048 						node = node.previousSibling;
7049 					}
7050 
7051 					self.remove(elm);
7052 				}
7053 
7054 				// Only set HTML on elements
7055 				if (elm.nodeType == 1) {
7056 					doc = doc || elm.ownerDocument || self.doc;
7057 
7058 					if (isIE) {
7059 						try {
7060 							// Try outerHTML for IE it sometimes produces an unknown runtime error
7061 							if (elm.nodeType == 1 && self.hasOuterHTML) {
7062 								elm.outerHTML = html;
7063 							} else {
7064 								set();
7065 							}
7066 						} catch (ex) {
7067 							// Fix for unknown runtime error
7068 							set();
7069 						}
7070 					} else {
7071 						set();
7072 					}
7073 				}
7074 			});
7075 		},
7076 
7077 		/**
7078 		 * Entity decodes a string. This method decodes any HTML entities, such as å.
7079 		 *
7080 		 * @method decode
7081 		 * @param {String} s String to decode entities on.
7082 		 * @return {String} Entity decoded string.
7083 		 */
7084 		decode: Entities.decode,
7085 
7086 		/**
7087 		 * Entity encodes a string. This method encodes the most common entities, such as <>"&.
7088 		 *
7089 		 * @method encode
7090 		 * @param {String} text String to encode with entities.
7091 		 * @return {String} Entity encoded string.
7092 		 */
7093 		encode: Entities.encodeAllRaw,
7094 
7095 		/**
7096 		 * Inserts an element after the reference element.
7097 		 *
7098 		 * @method insertAfter
7099 		 * @param {Element} node Element to insert after the reference.
7100 		 * @param {Element/String/Array} reference_node Reference element, element id or array of elements to insert after.
7101 		 * @return {Element/Array} Element that got added or an array with elements.
7102 		 */
7103 		insertAfter: function(node, reference_node) {
7104 			reference_node = this.get(reference_node);
7105 
7106 			return this.run(node, function(node) {
7107 				var parent, nextSibling;
7108 
7109 				parent = reference_node.parentNode;
7110 				nextSibling = reference_node.nextSibling;
7111 
7112 				if (nextSibling) {
7113 					parent.insertBefore(node, nextSibling);
7114 				} else {
7115 					parent.appendChild(node);
7116 				}
7117 
7118 				return node;
7119 			});
7120 		},
7121 
7122 		/**
7123 		 * Replaces the specified element or elements with the new element specified. The new element will
7124 		 * be cloned if multiple input elements are passed in.
7125 		 *
7126 		 * @method replace
7127 		 * @param {Element} newElm New element to replace old ones with.
7128 		 * @param {Element/String/Array} oldELm Element DOM node, element id or array of elements or ids to replace.
7129 		 * @param {Boolean} k Optional keep children state, if set to true child nodes from the old object will be added to new ones.
7130 		 */
7131 		replace: function(newElm, oldElm, keepChildren) {
7132 			var self = this;
7133 
7134 			return self.run(oldElm, function(oldElm) {
7135 				if (is(oldElm, 'array')) {
7136 					newElm = newElm.cloneNode(true);
7137 				}
7138 
7139 				if (keepChildren) {
7140 					each(grep(oldElm.childNodes), function(node) {
7141 						newElm.appendChild(node);
7142 					});
7143 				}
7144 
7145 				return oldElm.parentNode.replaceChild(newElm, oldElm);
7146 			});
7147 		},
7148 
7149 		/**
7150 		 * Renames the specified element and keeps its attributes and children.
7151 		 *
7152 		 * @method rename
7153 		 * @param {Element} elm Element to rename.
7154 		 * @param {String} name Name of the new element.
7155 		 * @return {Element} New element or the old element if it needed renaming.
7156 		 */
7157 		rename: function(elm, name) {
7158 			var self = this, newElm;
7159 
7160 			if (elm.nodeName != name.toUpperCase()) {
7161 				// Rename block element
7162 				newElm = self.create(name);
7163 
7164 				// Copy attribs to new block
7165 				each(self.getAttribs(elm), function(attr_node) {
7166 					self.setAttrib(newElm, attr_node.nodeName, self.getAttrib(elm, attr_node.nodeName));
7167 				});
7168 
7169 				// Replace block
7170 				self.replace(newElm, elm, 1);
7171 			}
7172 
7173 			return newElm || elm;
7174 		},
7175 
7176 		/**
7177 		 * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic.
7178 		 *
7179 		 * @method findCommonAncestor
7180 		 * @param {Element} a Element to find common ancestor of.
7181 		 * @param {Element} b Element to find common ancestor of.
7182 		 * @return {Element} Common ancestor element of the two input elements.
7183 		 */
7184 		findCommonAncestor: function(a, b) {
7185 			var ps = a, pe;
7186 
7187 			while (ps) {
7188 				pe = b;
7189 
7190 				while (pe && ps != pe) {
7191 					pe = pe.parentNode;
7192 				}
7193 
7194 				if (ps == pe) {
7195 					break;
7196 				}
7197 
7198 				ps = ps.parentNode;
7199 			}
7200 
7201 			if (!ps && a.ownerDocument) {
7202 				return a.ownerDocument.documentElement;
7203 			}
7204 
7205 			return ps;
7206 		},
7207 
7208 		/**
7209 		 * Parses the specified RGB color value and returns a hex version of that color.
7210 		 *
7211 		 * @method toHex
7212 		 * @param {String} rgbVal RGB string value like rgb(1,2,3)
7213 		 * @return {String} Hex version of that RGB value like #FF00FF.
7214 		 */
7215 		toHex: function(rgbVal) {
7216 			return this.styles.toHex(Tools.trim(rgbVal));
7217 		},
7218 
7219 		/**
7220 		 * Executes the specified function on the element by id or dom element node or array of elements/id.
7221 		 *
7222 		 * @method run
7223 		 * @param {String/Element/Array} Element ID or DOM element object or array with ids or elements.
7224 		 * @param {function} f Function to execute for each item.
7225 		 * @param {Object} s Optional scope to execute the function in.
7226 		 * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in.
7227 		 */
7228 		run: function(elm, func, scope) {
7229 			var self = this, result;
7230 
7231 			if (typeof(elm) === 'string') {
7232 				elm = self.get(elm);
7233 			}
7234 
7235 			if (!elm) {
7236 				return false;
7237 			}
7238 
7239 			scope = scope || this;
7240 			if (!elm.nodeType && (elm.length || elm.length === 0)) {
7241 				result = [];
7242 
7243 				each(elm, function(elm, i) {
7244 					if (elm) {
7245 						if (typeof(elm) == 'string') {
7246 							elm = self.get(elm);
7247 						}
7248 
7249 						result.push(func.call(scope, elm, i));
7250 					}
7251 				});
7252 
7253 				return result;
7254 			}
7255 
7256 			return func.call(scope, elm);
7257 		},
7258 
7259 		/**
7260 		 * Returns a NodeList with attributes for the element.
7261 		 *
7262 		 * @method getAttribs
7263 		 * @param {HTMLElement/string} elm Element node or string id to get attributes from.
7264 		 * @return {NodeList} NodeList with attributes.
7265 		 */
7266 		getAttribs: function(elm) {
7267 			var attrs;
7268 
7269 			elm = this.get(elm);
7270 
7271 			if (!elm) {
7272 				return [];
7273 			}
7274 
7275 			if (isIE) {
7276 				attrs = [];
7277 
7278 				// Object will throw exception in IE
7279 				if (elm.nodeName == 'OBJECT') {
7280 					return elm.attributes;
7281 				}
7282 
7283 				// IE doesn't keep the selected attribute if you clone option elements
7284 				if (elm.nodeName === 'OPTION' && this.getAttrib(elm, 'selected')) {
7285 					attrs.push({specified: 1, nodeName: 'selected'});
7286 				}
7287 
7288 				// It's crazy that this is faster in IE but it's because it returns all attributes all the time
7289 				var attrRegExp = /<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;
7290 				elm.cloneNode(false).outerHTML.replace(attrRegExp, '').replace(/[\w:\-]+/gi, function(a) {
7291 					attrs.push({specified: 1, nodeName: a});
7292 				});
7293 
7294 				return attrs;
7295 			}
7296 
7297 			return elm.attributes;
7298 		},
7299 
7300 		/**
7301 		 * Returns true/false if the specified node is to be considered empty or not.
7302 		 *
7303 		 * @example
7304 		 * tinymce.DOM.isEmpty(node, {img: true});
7305 		 * @method isEmpty
7306 		 * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements.
7307 		 * @return {Boolean} true/false if the node is empty or not.
7308 		 */
7309 		isEmpty: function(node, elements) {
7310 			var self = this, i, attributes, type, walker, name, brCount = 0;
7311 
7312 			node = node.firstChild;
7313 			if (node) {
7314 				walker = new TreeWalker(node, node.parentNode);
7315 				elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;
7316 
7317 				do {
7318 					type = node.nodeType;
7319 
7320 					if (type === 1) {
7321 						// Ignore bogus elements
7322 						if (node.getAttribute('data-mce-bogus')) {
7323 							continue;
7324 						}
7325 
7326 						// Keep empty elements like <img />
7327 						name = node.nodeName.toLowerCase();
7328 						if (elements && elements[name]) {
7329 							// Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>
7330 							if (name === 'br') {
7331 								brCount++;
7332 								continue;
7333 							}
7334 
7335 							return false;
7336 						}
7337 
7338 						// Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>
7339 						attributes = self.getAttribs(node);
7340 						i = attributes.length;
7341 						while (i--) {
7342 							name = attributes[i].nodeName;
7343 							if (name === "name" || name === 'data-mce-bookmark') {
7344 								return false;
7345 							}
7346 						}
7347 					}
7348 
7349 					// Keep comment nodes
7350 					if (type == 8) {
7351 						return false;
7352 					}
7353 
7354 					// Keep non whitespace text nodes
7355 					if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue))) {
7356 						return false;
7357 					}
7358 				} while ((node = walker.next()));
7359 			}
7360 
7361 			return brCount <= 1;
7362 		},
7363 
7364 		/**
7365 		 * Creates a new DOM Range object. This will use the native DOM Range API if it's
7366 		 * available. If it's not, it will fall back to the custom TinyMCE implementation.
7367 		 *
7368 		 * @method createRng
7369 		 * @return {DOMRange} DOM Range object.
7370 		 * @example
7371 		 * var rng = tinymce.DOM.createRng();
7372 		 * alert(rng.startContainer + "," + rng.startOffset);
7373 		 */
7374 		createRng: function() {
7375 			var doc = this.doc;
7376 
7377 			return doc.createRange ? doc.createRange() : new Range(this);
7378 		},
7379 
7380 		/**
7381 		 * Returns the index of the specified node within its parent.
7382 		 *
7383 		 * @method nodeIndex
7384 		 * @param {Node} node Node to look for.
7385 		 * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization.
7386 		 * @return {Number} Index of the specified node.
7387 		 */
7388 		nodeIndex: function(node, normalized) {
7389 			var idx = 0, lastNodeType, nodeType;
7390 
7391 			if (node) {
7392 				for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) {
7393 					nodeType = node.nodeType;
7394 
7395 					// Normalize text nodes
7396 					if (normalized && nodeType == 3) {
7397 						if (nodeType == lastNodeType || !node.nodeValue.length) {
7398 							continue;
7399 						}
7400 					}
7401 					idx++;
7402 					lastNodeType = nodeType;
7403 				}
7404 			}
7405 
7406 			return idx;
7407 		},
7408 
7409 		/**
7410 		 * Splits an element into two new elements and places the specified split
7411 		 * element or elements between the new ones. For example splitting the paragraph at the bold element in
7412 		 * this example <p>abc<b>abc</b>123</p> would produce <p>abc</p><b>abc</b><p>123</p>.
7413 		 *
7414 		 * @method split
7415 		 * @param {Element} parentElm Parent element to split.
7416 		 * @param {Element} splitElm Element to split at.
7417 		 * @param {Element} replacementElm Optional replacement element to replace the split element with.
7418 		 * @return {Element} Returns the split element or the replacement element if that is specified.
7419 		 */
7420 		split: function(parentElm, splitElm, replacementElm) {
7421 			var self = this, r = self.createRng(), bef, aft, pa;
7422 
7423 			// W3C valid browsers tend to leave empty nodes to the left/right side of the contents - this makes sense
7424 			// but we don't want that in our code since it serves no purpose for the end user
7425 			// For example splitting this html at the bold element:
7426 			//   <p>text 1<span><b>CHOP</b></span>text 2</p>
7427 			// would produce:
7428 			//   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
7429 			// this function will then trim off empty edges and produce:
7430 			//   <p>text 1</p><b>CHOP</b><p>text 2</p>
7431 			function trimNode(node) {
7432 				var i, children = node.childNodes, type = node.nodeType;
7433 
7434 				function surroundedBySpans(node) {
7435 					var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
7436 					var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
7437 					return previousIsSpan && nextIsSpan;
7438 				}
7439 
7440 				if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark') {
7441 					return;
7442 				}
7443 
7444 				for (i = children.length - 1; i >= 0; i--) {
7445 					trimNode(children[i]);
7446 				}
7447 
7448 				if (type != 9) {
7449 					// Keep non whitespace text nodes
7450 					if (type == 3 && node.nodeValue.length > 0) {
7451 						// If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"
7452 						// Also keep text nodes with only spaces if surrounded by spans.
7453 						// eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
7454 						var trimmedLength = trim(node.nodeValue).length;
7455 						if (!self.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node)) {
7456 							return;
7457 						}
7458 					} else if (type == 1) {
7459 						// If the only child is a bookmark then move it up
7460 						children = node.childNodes;
7461 
7462 						// TODO fix this complex if
7463 						if (children.length == 1 && children[0] && children[0].nodeType == 1 &&
7464 							children[0].getAttribute('data-mce-type') == 'bookmark') {
7465 							node.parentNode.insertBefore(children[0], node);
7466 						}
7467 
7468 						// Keep non empty elements or img, hr etc
7469 						if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) {
7470 							return;
7471 						}
7472 					}
7473 
7474 					self.remove(node);
7475 				}
7476 
7477 				return node;
7478 			}
7479 
7480 			if (parentElm && splitElm) {
7481 				// Get before chunk
7482 				r.setStart(parentElm.parentNode, self.nodeIndex(parentElm));
7483 				r.setEnd(splitElm.parentNode, self.nodeIndex(splitElm));
7484 				bef = r.extractContents();
7485 
7486 				// Get after chunk
7487 				r = self.createRng();
7488 				r.setStart(splitElm.parentNode, self.nodeIndex(splitElm) + 1);
7489 				r.setEnd(parentElm.parentNode, self.nodeIndex(parentElm) + 1);
7490 				aft = r.extractContents();
7491 
7492 				// Insert before chunk
7493 				pa = parentElm.parentNode;
7494 				pa.insertBefore(trimNode(bef), parentElm);
7495 
7496 				// Insert middle chunk
7497 				if (replacementElm) {
7498 					pa.replaceChild(replacementElm, splitElm);
7499 				} else {
7500 					pa.insertBefore(splitElm, parentElm);
7501 				}
7502 
7503 				// Insert after chunk
7504 				pa.insertBefore(trimNode(aft), parentElm);
7505 				self.remove(parentElm);
7506 
7507 				return replacementElm || splitElm;
7508 			}
7509 		},
7510 
7511 		/**
7512 		 * Adds an event handler to the specified object.
7513 		 *
7514 		 * @method bind
7515 		 * @param {Element/Document/Window/Array} target Target element to bind events to.
7516 		 * handler to or an array of elements/ids/documents.
7517 		 * @param {String} name Name of event handler to add, for example: click.
7518 		 * @param {function} func Function to execute when the event occurs.
7519 		 * @param {Object} scope Optional scope to execute the function in.
7520 		 * @return {function} Function callback handler the same as the one passed in.
7521 		 */
7522 		bind: function(target, name, func, scope) {
7523 			var self = this;
7524 
7525 			if (Tools.isArray(target)) {
7526 				var i = target.length;
7527 
7528 				while (i--) {
7529 					target[i] = self.bind(target[i], name, func, scope);
7530 				}
7531 
7532 				return target;
7533 			}
7534 
7535 			// Collect all window/document events bound by editor instance
7536 			if (self.settings.collect && (target === self.doc || target === self.win)) {
7537 				self.boundEvents.push([target, name, func, scope]);
7538 			}
7539 
7540 			return self.events.bind(target, name, func, scope || self);
7541 		},
7542 
7543 		/**
7544 		 * Removes the specified event handler by name and function from an element or collection of elements.
7545 		 *
7546 		 * @method unbind
7547 		 * @param {Element/Document/Window/Array} target Target element to unbind events on.
7548 		 * @param {String} name Event handler name, for example: "click"
7549 		 * @param {function} func Function to remove.
7550 		 * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements
7551 		 * were passed in.
7552 		 */
7553 		unbind: function(target, name, func) {
7554 			var self = this, i;
7555 
7556 			if (Tools.isArray(target)) {
7557 				i = target.length;
7558 
7559 				while (i--) {
7560 					target[i] = self.unbind(target[i], name, func);
7561 				}
7562 
7563 				return target;
7564 			}
7565 
7566 			// Remove any bound events matching the input
7567 			if (self.boundEvents && (target === self.doc || target === self.win)) {
7568 				i = self.boundEvents.length;
7569 
7570 				while (i--) {
7571 					var item = self.boundEvents[i];
7572 
7573 					if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) {
7574 						this.events.unbind(item[0], item[1], item[2]);
7575 					}
7576 				}
7577 			}
7578 
7579 			return this.events.unbind(target, name, func);
7580 		},
7581 
7582 		/**
7583 		 * Fires the specified event name with object on target.
7584 		 *
7585 		 * @method fire
7586 		 * @param {Node/Document/Window} target Target element or object to fire event on.
7587 		 * @param {String} name Name of the event to fire.
7588 		 * @param {Object} evt Event object to send.
7589 		 * @return {Event} Event object.
7590 		 */
7591 		fire: function(target, name, evt) {
7592 			return this.events.fire(target, name, evt);
7593 		},
7594 
7595 		// Returns the content editable state of a node
7596 		getContentEditable: function(node) {
7597 			var contentEditable;
7598 
7599 			// Check type
7600 			if (!node || node.nodeType != 1) {
7601 				return null;
7602 			}
7603 
7604 			// Check for fake content editable
7605 			contentEditable = node.getAttribute("data-mce-contenteditable");
7606 			if (contentEditable && contentEditable !== "inherit") {
7607 				return contentEditable;
7608 			}
7609 
7610 			// Check for real content editable
7611 			return node.contentEditable !== "inherit" ? node.contentEditable : null;
7612 		},
7613 
7614 		getContentEditableParent: function(node) {
7615 			var root = this.getRoot(), state = null;
7616 
7617 			for (; node && node !== root; node = node.parentNode) {
7618 				state = this.getContentEditable(node);
7619 
7620 				if (state !== null) {
7621 					break;
7622 				}
7623 			}
7624 
7625 			return state;
7626 		},
7627 
7628 		/**
7629 		 * Destroys all internal references to the DOM to solve IE leak issues.
7630 		 *
7631 		 * @method destroy
7632 		 */
7633 		destroy: function() {
7634 			var self = this;
7635 
7636 			// Unbind all events bound to window/document by editor instance
7637 			if (self.boundEvents) {
7638 				var i = self.boundEvents.length;
7639 
7640 				while (i--) {
7641 					var item = self.boundEvents[i];
7642 					this.events.unbind(item[0], item[1], item[2]);
7643 				}
7644 
7645 				self.boundEvents = null;
7646 			}
7647 
7648 			// Restore sizzle document to window.document
7649 			// Since the current document might be removed producing "Permission denied" on IE see #6325
7650 			if (Sizzle.setDocument) {
7651 				Sizzle.setDocument();
7652 			}
7653 
7654 			self.win = self.doc = self.root = self.events = self.frag = null;
7655 		},
7656 
7657 		isChildOf: function(node, parent) {
7658 			if (parent.contains) {
7659 				return parent.contains(node);
7660 			}
7661 
7662 			while (node) {
7663 				if (parent === node) {
7664 					return true;
7665 				}
7666 
7667 				node = node.parentNode;
7668 			}
7669 
7670 			return false;
7671 		},
7672 
7673 		// #ifdef debug
7674 
7675 		dumpRng: function(r) {
7676 			return (
7677 				'startContainer: ' + r.startContainer.nodeName +
7678 				', startOffset: ' + r.startOffset +
7679 				', endContainer: ' + r.endContainer.nodeName +
7680 				', endOffset: ' + r.endOffset
7681 			);
7682 		},
7683 
7684 		// #endif
7685 
7686 		_findSib: function(node, selector, name) {
7687 			var self = this, func = selector;
7688 
7689 			if (node) {
7690 				// If expression make a function of it using is
7691 				if (typeof(func) == 'string') {
7692 					func = function(node) {
7693 						return self.is(node, selector);
7694 					};
7695 				}
7696 
7697 				// Loop all siblings
7698 				for (node = node[name]; node; node = node[name]) {
7699 					if (func(node)) {
7700 						return node;
7701 					}
7702 				}
7703 			}
7704 
7705 			return null;
7706 		}
7707 	};
7708 
7709 	/**
7710 	 * Instance of DOMUtils for the current document.
7711 	 *
7712 	 * @static
7713 	 * @property DOM
7714 	 * @type tinymce.dom.DOMUtils
7715 	 * @example
7716 	 * // Example of how to add a class to some element by id
7717 	 * tinymce.DOM.addClass('someid', 'someclass');
7718 	 */
7719 	DOMUtils.DOM = new DOMUtils(document);
7720 
7721 	return DOMUtils;
7722 });
7723 
7724 // Included from: js/tinymce/classes/dom/ScriptLoader.js
7725 
7726 /**
7727  * ScriptLoader.js
7728  *
7729  * Copyright, Moxiecode Systems AB
7730  * Released under LGPL License.
7731  *
7732  * License: http://www.tinymce.com/license
7733  * Contributing: http://www.tinymce.com/contributing
7734  */
7735 
7736 /*globals console*/
7737 
7738 /**
7739  * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks
7740  * when various items gets loaded. This class is useful to load external JavaScript files.
7741  *
7742  * @class tinymce.dom.ScriptLoader
7743  * @example
7744  * // Load a script from a specific URL using the global script loader
7745  * tinymce.ScriptLoader.load('somescript.js');
7746  *
7747  * // Load a script using a unique instance of the script loader
7748  * var scriptLoader = new tinymce.dom.ScriptLoader();
7749  *
7750  * scriptLoader.load('somescript.js');
7751  *
7752  * // Load multiple scripts
7753  * var scriptLoader = new tinymce.dom.ScriptLoader();
7754  *
7755  * scriptLoader.add('somescript1.js');
7756  * scriptLoader.add('somescript2.js');
7757  * scriptLoader.add('somescript3.js');
7758  *
7759  * scriptLoader.loadQueue(function() {
7760  *    alert('All scripts are now loaded.');
7761  * });
7762  */
7763 define("tinymce/dom/ScriptLoader", [
7764 	"tinymce/dom/DOMUtils",
7765 	"tinymce/util/Tools"
7766 ], function(DOMUtils, Tools) {
7767 	var DOM = DOMUtils.DOM;
7768 	var each = Tools.each, grep = Tools.grep;
7769 
7770 	function ScriptLoader() {
7771 		var QUEUED = 0,
7772 			LOADING = 1,
7773 			LOADED = 2,
7774 			states = {},
7775 			queue = [],
7776 			scriptLoadedCallbacks = {},
7777 			queueLoadedCallbacks = [],
7778 			loading = 0,
7779 			undef;
7780 
7781 		/**
7782 		 * Loads a specific script directly without adding it to the load queue.
7783 		 *
7784 		 * @method load
7785 		 * @param {String} url Absolute URL to script to add.
7786 		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
7787 		 * @param {Object} scope Optional scope to execute callback in.
7788 		 */
7789 		function loadScript(url, callback) {
7790 			var dom = DOM, elm, id;
7791 
7792 			// Execute callback when script is loaded
7793 			function done() {
7794 				dom.remove(id);
7795 
7796 				if (elm) {
7797 					elm.onreadystatechange = elm.onload = elm = null;
7798 				}
7799 
7800 				callback();
7801 			}
7802 
7803 			function error() {
7804 				/*eslint no-console:0 */
7805 
7806 				// Report the error so it's easier for people to spot loading errors
7807 				if (typeof(console) !== "undefined" && console.log) {
7808 					console.log("Failed to load: " + url);
7809 				}
7810 
7811 				// We can't mark it as done if there is a load error since
7812 				// A) We don't want to produce 404 errors on the server and
7813 				// B) the onerror event won't fire on all browsers.
7814 				// done();
7815 			}
7816 
7817 			id = dom.uniqueId();
7818 
7819 			// Create new script element
7820 			elm = document.createElement('script');
7821 			elm.id = id;
7822 			elm.type = 'text/javascript';
7823 			elm.src = url;
7824 
7825 			// Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly
7826 			if ("onreadystatechange" in elm) {
7827 				elm.onreadystatechange = function() {
7828 					if (/loaded|complete/.test(elm.readyState)) {
7829 						done();
7830 					}
7831 				};
7832 			} else {
7833 				elm.onload = done;
7834 			}
7835 
7836 			// Add onerror event will get fired on some browsers but not all of them
7837 			elm.onerror = error;
7838 
7839 			// Add script to document
7840 			(document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
7841 		}
7842 
7843 		/**
7844 		 * Returns true/false if a script has been loaded or not.
7845 		 *
7846 		 * @method isDone
7847 		 * @param {String} url URL to check for.
7848 		 * @return {Boolean} true/false if the URL is loaded.
7849 		 */
7850 		this.isDone = function(url) {
7851 			return states[url] == LOADED;
7852 		};
7853 
7854 		/**
7855 		 * Marks a specific script to be loaded. This can be useful if a script got loaded outside
7856 		 * the script loader or to skip it from loading some script.
7857 		 *
7858 		 * @method markDone
7859 		 * @param {string} u Absolute URL to the script to mark as loaded.
7860 		 */
7861 		this.markDone = function(url) {
7862 			states[url] = LOADED;
7863 		};
7864 
7865 		/**
7866 		 * Adds a specific script to the load queue of the script loader.
7867 		 *
7868 		 * @method add
7869 		 * @param {String} url Absolute URL to script to add.
7870 		 * @param {function} callback Optional callback function to execute ones this script gets loaded.
7871 		 * @param {Object} scope Optional scope to execute callback in.
7872 		 */
7873 		this.add = this.load = function(url, callback, scope) {
7874 			var state = states[url];
7875 
7876 			// Add url to load queue
7877 			if (state == undef) {
7878 				queue.push(url);
7879 				states[url] = QUEUED;
7880 			}
7881 
7882 			if (callback) {
7883 				// Store away callback for later execution
7884 				if (!scriptLoadedCallbacks[url]) {
7885 					scriptLoadedCallbacks[url] = [];
7886 				}
7887 
7888 				scriptLoadedCallbacks[url].push({
7889 					func: callback,
7890 					scope: scope || this
7891 				});
7892 			}
7893 		};
7894 
7895 		/**
7896 		 * Starts the loading of the queue.
7897 		 *
7898 		 * @method loadQueue
7899 		 * @param {function} callback Optional callback to execute when all queued items are loaded.
7900 		 * @param {Object} scope Optional scope to execute the callback in.
7901 		 */
7902 		this.loadQueue = function(callback, scope) {
7903 			this.loadScripts(queue, callback, scope);
7904 		};
7905 
7906 		/**
7907 		 * Loads the specified queue of files and executes the callback ones they are loaded.
7908 		 * This method is generally not used outside this class but it might be useful in some scenarios.
7909 		 *
7910 		 * @method loadScripts
7911 		 * @param {Array} scripts Array of queue items to load.
7912 		 * @param {function} callback Optional callback to execute ones all items are loaded.
7913 		 * @param {Object} scope Optional scope to execute callback in.
7914 		 */
7915 		this.loadScripts = function(scripts, callback, scope) {
7916 			var loadScripts;
7917 
7918 			function execScriptLoadedCallbacks(url) {
7919 				// Execute URL callback functions
7920 				each(scriptLoadedCallbacks[url], function(callback) {
7921 					callback.func.call(callback.scope);
7922 				});
7923 
7924 				scriptLoadedCallbacks[url] = undef;
7925 			}
7926 
7927 			queueLoadedCallbacks.push({
7928 				func: callback,
7929 				scope: scope || this
7930 			});
7931 
7932 			loadScripts = function() {
7933 				var loadingScripts = grep(scripts);
7934 
7935 				// Current scripts has been handled
7936 				scripts.length = 0;
7937 
7938 				// Load scripts that needs to be loaded
7939 				each(loadingScripts, function(url) {
7940 					// Script is already loaded then execute script callbacks directly
7941 					if (states[url] == LOADED) {
7942 						execScriptLoadedCallbacks(url);
7943 						return;
7944 					}
7945 
7946 					// Is script not loading then start loading it
7947 					if (states[url] != LOADING) {
7948 						states[url] = LOADING;
7949 						loading++;
7950 
7951 						loadScript(url, function() {
7952 							states[url] = LOADED;
7953 							loading--;
7954 
7955 							execScriptLoadedCallbacks(url);
7956 
7957 							// Load more scripts if they where added by the recently loaded script
7958 							loadScripts();
7959 						});
7960 					}
7961 				});
7962 
7963 				// No scripts are currently loading then execute all pending queue loaded callbacks
7964 				if (!loading) {
7965 					each(queueLoadedCallbacks, function(callback) {
7966 						callback.func.call(callback.scope);
7967 					});
7968 
7969 					queueLoadedCallbacks.length = 0;
7970 				}
7971 			};
7972 
7973 			loadScripts();
7974 		};
7975 	}
7976 
7977 	ScriptLoader.ScriptLoader = new ScriptLoader();
7978 
7979 	return ScriptLoader;
7980 });
7981 
7982 // Included from: js/tinymce/classes/AddOnManager.js
7983 
7984 /**
7985  * AddOnManager.js
7986  *
7987  * Copyright, Moxiecode Systems AB
7988  * Released under LGPL License.
7989  *
7990  * License: http://www.tinymce.com/license
7991  * Contributing: http://www.tinymce.com/contributing
7992  */
7993 
7994 /**
7995  * This class handles the loading of themes/plugins or other add-ons and their language packs.
7996  *
7997  * @class tinymce.AddOnManager
7998  */
7999 define("tinymce/AddOnManager", [
8000 	"tinymce/dom/ScriptLoader",
8001 	"tinymce/util/Tools"
8002 ], function(ScriptLoader, Tools) {
8003 	var each = Tools.each;
8004 
8005 	function AddOnManager() {
8006 		var self = this;
8007 
8008 		self.items = [];
8009 		self.urls = {};
8010 		self.lookup = {};
8011 	}
8012 
8013 	AddOnManager.prototype = {
8014 		/**
8015 		 * Returns the specified add on by the short name.
8016 		 *
8017 		 * @method get
8018 		 * @param {String} name Add-on to look for.
8019 		 * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined.
8020 		 */
8021 		get: function(name) {
8022 			if (this.lookup[name]) {
8023 				return this.lookup[name].instance;
8024 			} else {
8025 				return undefined;
8026 			}
8027 		},
8028 
8029 		dependencies: function(name) {
8030 			var result;
8031 
8032 			if (this.lookup[name]) {
8033 				result = this.lookup[name].dependencies;
8034 			}
8035 
8036 			return result || [];
8037 		},
8038 
8039 		/**
8040 		 * Loads a language pack for the specified add-on.
8041 		 *
8042 		 * @method requireLangPack
8043 		 * @param {String} name Short name of the add-on.
8044 		 * @param {String} languages Optional comma or space separated list of languages to check if it matches the name.
8045 		 */
8046 		requireLangPack: function(name, languages) {
8047 			var language = AddOnManager.language;
8048 
8049 			if (language && AddOnManager.languageLoad !== false) {
8050 				if (languages) {
8051 					languages = ',' + languages + ',';
8052 
8053 					// Load short form sv.js or long form sv_SE.js
8054 					if (languages.indexOf(',' + language.substr(0, 2) + ',') != -1) {
8055 						language = language.substr(0, 2);
8056 					} else if (languages.indexOf(',' + language + ',') == -1) {
8057 						return;
8058 					}
8059 				}
8060 
8061 				ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + language + '.js');
8062 			}
8063 		},
8064 
8065 		/**
8066 		 * Adds a instance of the add-on by it's short name.
8067 		 *
8068 		 * @method add
8069 		 * @param {String} id Short name/id for the add-on.
8070 		 * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
8071 		 * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
8072 		 * @example
8073 		 * // Create a simple plugin
8074 		 * tinymce.create('tinymce.plugins.TestPlugin', {
8075 		 *   TestPlugin: function(ed, url) {
8076 		 *   ed.on('click', function(e) {
8077 		 *      ed.windowManager.alert('Hello World!');
8078 		 *   });
8079 		 *   }
8080 		 * });
8081 		 *
8082 		 * // Register plugin using the add method
8083 		 * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin);
8084 		 *
8085 		 * // Initialize TinyMCE
8086 		 * tinymce.init({
8087 		 *  ...
8088 		 *  plugins: '-test' // Init the plugin but don't try to load it
8089 		 * });
8090 		 */
8091 		add: function(id, addOn, dependencies) {
8092 			this.items.push(addOn);
8093 			this.lookup[id] = {instance: addOn, dependencies: dependencies};
8094 
8095 			return addOn;
8096 		},
8097 
8098 		createUrl: function(baseUrl, dep) {
8099 			if (typeof dep === "object") {
8100 				return dep;
8101 			} else {
8102 				return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
8103 			}
8104 		},
8105 
8106 		/**
8107 		 * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url.
8108 		 * This should be used in development mode.  A new compressor/javascript munger process will ensure that the
8109 		 * components are put together into the plugin.js file and compressed correctly.
8110 		 *
8111 		 * @method addComponents
8112 		 * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins).
8113 		 * @param {Array} scripts Array containing the names of the scripts to load.
8114 		 */
8115 		addComponents: function(pluginName, scripts) {
8116 			var pluginUrl = this.urls[pluginName];
8117 
8118 			each(scripts, function(script) {
8119 				ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script);
8120 			});
8121 		},
8122 
8123 		/**
8124 		 * Loads an add-on from a specific url.
8125 		 *
8126 		 * @method load
8127 		 * @param {String} name Short name of the add-on that gets loaded.
8128 		 * @param {String} addOnUrl URL to the add-on that will get loaded.
8129 		 * @param {function} callback Optional callback to execute ones the add-on is loaded.
8130 		 * @param {Object} scope Optional scope to execute the callback in.
8131 		 * @example
8132 		 * // Loads a plugin from an external URL
8133 		 * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
8134 		 *
8135 		 * // Initialize TinyMCE
8136 		 * tinymce.init({
8137 		 *  ...
8138 		 *  plugins: '-myplugin' // Don't try to load it again
8139 		 * });
8140 		 */
8141 		load: function(name, addOnUrl, callback, scope) {
8142 			var self = this, url = addOnUrl;
8143 
8144 			function loadDependencies() {
8145 				var dependencies = self.dependencies(name);
8146 
8147 				each(dependencies, function(dep) {
8148 					var newUrl = self.createUrl(addOnUrl, dep);
8149 
8150 					self.load(newUrl.resource, newUrl, undefined, undefined);
8151 				});
8152 
8153 				if (callback) {
8154 					if (scope) {
8155 						callback.call(scope);
8156 					} else {
8157 						callback.call(ScriptLoader);
8158 					}
8159 				}
8160 			}
8161 
8162 			if (self.urls[name]) {
8163 				return;
8164 			}
8165 
8166 			if (typeof addOnUrl === "object") {
8167 				url = addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix;
8168 			}
8169 
8170 			if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) {
8171 				url = AddOnManager.baseURL + '/' + url;
8172 			}
8173 
8174 			self.urls[name] = url.substring(0, url.lastIndexOf('/'));
8175 
8176 			if (self.lookup[name]) {
8177 				loadDependencies();
8178 			} else {
8179 				ScriptLoader.ScriptLoader.add(url, loadDependencies, scope);
8180 			}
8181 		}
8182 	};
8183 
8184 	AddOnManager.PluginManager = new AddOnManager();
8185 	AddOnManager.ThemeManager = new AddOnManager();
8186 
8187 	return AddOnManager;
8188 });
8189 
8190 /**
8191  * TinyMCE theme class.
8192  *
8193  * @class tinymce.Theme
8194  */
8195 
8196 /**
8197  * This method is responsible for rendering/generating the overall user interface with toolbars, buttons, iframe containers etc.
8198  *
8199  * @method renderUI
8200  * @param {Object} obj Object parameter containing the targetNode DOM node that will be replaced visually with an editor instance.
8201  * @return {Object} an object with items like iframeContainer, editorContainer, sizeContainer, deltaWidth, deltaHeight.
8202  */
8203 
8204 /**
8205  * Plugin base class, this is a pseudo class that describes how a plugin is to be created for TinyMCE. The methods below are all optional.
8206  *
8207  * @class tinymce.Plugin
8208  * @example
8209  * tinymce.PluginManager.add('example', function(editor, url) {
8210  *     // Add a button that opens a window
8211  *     editor.addButton('example', {
8212  *         text: 'My button',
8213  *         icon: false,
8214  *         onclick: function() {
8215  *             // Open window
8216  *             editor.windowManager.open({
8217  *                 title: 'Example plugin',
8218  *                 body: [
8219  *                     {type: 'textbox', name: 'title', label: 'Title'}
8220  *                 ],
8221  *                 onsubmit: function(e) {
8222  *                     // Insert content when the window form is submitted
8223  *                     editor.insertContent('Title: ' + e.data.title);
8224  *                 }
8225  *             });
8226  *         }
8227  *     });
8228  *
8229  *     // Adds a menu item to the tools menu
8230  *     editor.addMenuItem('example', {
8231  *         text: 'Example plugin',
8232  *         context: 'tools',
8233  *         onclick: function() {
8234  *             // Open window with a specific url
8235  *             editor.windowManager.open({
8236  *                 title: 'TinyMCE site',
8237  *                 url: 'http://www.tinymce.com',
8238  *                 width: 800,
8239  *                 height: 600,
8240  *                 buttons: [{
8241  *                     text: 'Close',
8242  *                     onclick: 'close'
8243  *                 }]
8244  *             });
8245  *         }
8246  *     });
8247  * });
8248  */
8249 
8250 // Included from: js/tinymce/classes/html/Node.js
8251 
8252 /**
8253  * Node.js
8254  *
8255  * Copyright, Moxiecode Systems AB
8256  * Released under LGPL License.
8257  *
8258  * License: http://www.tinymce.com/license
8259  * Contributing: http://www.tinymce.com/contributing
8260  */
8261 
8262 /**
8263  * This class is a minimalistic implementation of a DOM like node used by the DomParser class.
8264  *
8265  * @example
8266  * var node = new tinymce.html.Node('strong', 1);
8267  * someRoot.append(node);
8268  *
8269  * @class tinymce.html.Node
8270  * @version 3.4
8271  */
8272 define("tinymce/html/Node", [], function() {
8273 	var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
8274 		'#text': 3,
8275 		'#comment': 8,
8276 		'#cdata': 4,
8277 		'#pi': 7,
8278 		'#doctype': 10,
8279 		'#document-fragment': 11
8280 	};
8281 
8282 	// Walks the tree left/right
8283 	function walk(node, root_node, prev) {
8284 		var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
8285 
8286 		// Walk into nodes if it has a start
8287 		if (node[startName]) {
8288 			return node[startName];
8289 		}
8290 
8291 		// Return the sibling if it has one
8292 		if (node !== root_node) {
8293 			sibling = node[siblingName];
8294 
8295 			if (sibling) {
8296 				return sibling;
8297 			}
8298 
8299 			// Walk up the parents to look for siblings
8300 			for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
8301 				sibling = parent[siblingName];
8302 
8303 				if (sibling) {
8304 					return sibling;
8305 				}
8306 			}
8307 		}
8308 	}
8309 
8310 	/**
8311 	 * Constructs a new Node instance.
8312 	 *
8313 	 * @constructor
8314 	 * @method Node
8315 	 * @param {String} name Name of the node type.
8316 	 * @param {Number} type Numeric type representing the node.
8317 	 */
8318 	function Node(name, type) {
8319 		this.name = name;
8320 		this.type = type;
8321 
8322 		if (type === 1) {
8323 			this.attributes = [];
8324 			this.attributes.map = {};
8325 		}
8326 	}
8327 
8328 	Node.prototype = {
8329 		/**
8330 		 * Replaces the current node with the specified one.
8331 		 *
8332 		 * @example
8333 		 * someNode.replace(someNewNode);
8334 		 *
8335 		 * @method replace
8336 		 * @param {tinymce.html.Node} node Node to replace the current node with.
8337 		 * @return {tinymce.html.Node} The old node that got replaced.
8338 		 */
8339 		replace: function(node) {
8340 			var self = this;
8341 
8342 			if (node.parent) {
8343 				node.remove();
8344 			}
8345 
8346 			self.insert(node, self);
8347 			self.remove();
8348 
8349 			return self;
8350 		},
8351 
8352 		/**
8353 		 * Gets/sets or removes an attribute by name.
8354 		 *
8355 		 * @example
8356 		 * someNode.attr("name", "value"); // Sets an attribute
8357 		 * console.log(someNode.attr("name")); // Gets an attribute
8358 		 * someNode.attr("name", null); // Removes an attribute
8359 		 *
8360 		 * @method attr
8361 		 * @param {String} name Attribute name to set or get.
8362 		 * @param {String} value Optional value to set.
8363 		 * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation.
8364 		 */
8365 		attr: function(name, value) {
8366 			var self = this, attrs, i, undef;
8367 
8368 			if (typeof name !== "string") {
8369 				for (i in name) {
8370 					self.attr(i, name[i]);
8371 				}
8372 
8373 				return self;
8374 			}
8375 
8376 			if ((attrs = self.attributes)) {
8377 				if (value !== undef) {
8378 					// Remove attribute
8379 					if (value === null) {
8380 						if (name in attrs.map) {
8381 							delete attrs.map[name];
8382 
8383 							i = attrs.length;
8384 							while (i--) {
8385 								if (attrs[i].name === name) {
8386 									attrs = attrs.splice(i, 1);
8387 									return self;
8388 								}
8389 							}
8390 						}
8391 
8392 						return self;
8393 					}
8394 
8395 					// Set attribute
8396 					if (name in attrs.map) {
8397 						// Set attribute
8398 						i = attrs.length;
8399 						while (i--) {
8400 							if (attrs[i].name === name) {
8401 								attrs[i].value = value;
8402 								break;
8403 							}
8404 						}
8405 					} else {
8406 						attrs.push({name: name, value: value});
8407 					}
8408 
8409 					attrs.map[name] = value;
8410 
8411 					return self;
8412 				} else {
8413 					return attrs.map[name];
8414 				}
8415 			}
8416 		},
8417 
8418 		/**
8419 		 * Does a shallow clones the node into a new node. It will also exclude id attributes since
8420 		 * there should only be one id per document.
8421 		 *
8422 		 * @example
8423 		 * var clonedNode = node.clone();
8424 		 *
8425 		 * @method clone
8426 		 * @return {tinymce.html.Node} New copy of the original node.
8427 		 */
8428 		clone: function() {
8429 			var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
8430 
8431 			// Clone element attributes
8432 			if ((selfAttrs = self.attributes)) {
8433 				cloneAttrs = [];
8434 				cloneAttrs.map = {};
8435 
8436 				for (i = 0, l = selfAttrs.length; i < l; i++) {
8437 					selfAttr = selfAttrs[i];
8438 
8439 					// Clone everything except id
8440 					if (selfAttr.name !== 'id') {
8441 						cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
8442 						cloneAttrs.map[selfAttr.name] = selfAttr.value;
8443 					}
8444 				}
8445 
8446 				clone.attributes = cloneAttrs;
8447 			}
8448 
8449 			clone.value = self.value;
8450 			clone.shortEnded = self.shortEnded;
8451 
8452 			return clone;
8453 		},
8454 
8455 		/**
8456 		 * Wraps the node in in another node.
8457 		 *
8458 		 * @example
8459 		 * node.wrap(wrapperNode);
8460 		 *
8461 		 * @method wrap
8462 		 */
8463 		wrap: function(wrapper) {
8464 			var self = this;
8465 
8466 			self.parent.insert(wrapper, self);
8467 			wrapper.append(self);
8468 
8469 			return self;
8470 		},
8471 
8472 		/**
8473 		 * Unwraps the node in other words it removes the node but keeps the children.
8474 		 *
8475 		 * @example
8476 		 * node.unwrap();
8477 		 *
8478 		 * @method unwrap
8479 		 */
8480 		unwrap: function() {
8481 			var self = this, node, next;
8482 
8483 			for (node = self.firstChild; node; ) {
8484 				next = node.next;
8485 				self.insert(node, self, true);
8486 				node = next;
8487 			}
8488 
8489 			self.remove();
8490 		},
8491 
8492 		/**
8493 		 * Removes the node from it's parent.
8494 		 *
8495 		 * @example
8496 		 * node.remove();
8497 		 *
8498 		 * @method remove
8499 		 * @return {tinymce.html.Node} Current node that got removed.
8500 		 */
8501 		remove: function() {
8502 			var self = this, parent = self.parent, next = self.next, prev = self.prev;
8503 
8504 			if (parent) {
8505 				if (parent.firstChild === self) {
8506 					parent.firstChild = next;
8507 
8508 					if (next) {
8509 						next.prev = null;
8510 					}
8511 				} else {
8512 					prev.next = next;
8513 				}
8514 
8515 				if (parent.lastChild === self) {
8516 					parent.lastChild = prev;
8517 
8518 					if (prev) {
8519 						prev.next = null;
8520 					}
8521 				} else {
8522 					next.prev = prev;
8523 				}
8524 
8525 				self.parent = self.next = self.prev = null;
8526 			}
8527 
8528 			return self;
8529 		},
8530 
8531 		/**
8532 		 * Appends a new node as a child of the current node.
8533 		 *
8534 		 * @example
8535 		 * node.append(someNode);
8536 		 *
8537 		 * @method append
8538 		 * @param {tinymce.html.Node} node Node to append as a child of the current one.
8539 		 * @return {tinymce.html.Node} The node that got appended.
8540 		 */
8541 		append: function(node) {
8542 			var self = this, last;
8543 
8544 			if (node.parent) {
8545 				node.remove();
8546 			}
8547 
8548 			last = self.lastChild;
8549 			if (last) {
8550 				last.next = node;
8551 				node.prev = last;
8552 				self.lastChild = node;
8553 			} else {
8554 				self.lastChild = self.firstChild = node;
8555 			}
8556 
8557 			node.parent = self;
8558 
8559 			return node;
8560 		},
8561 
8562 		/**
8563 		 * Inserts a node at a specific position as a child of the current node.
8564 		 *
8565 		 * @example
8566 		 * parentNode.insert(newChildNode, oldChildNode);
8567 		 *
8568 		 * @method insert
8569 		 * @param {tinymce.html.Node} node Node to insert as a child of the current node.
8570 		 * @param {tinymce.html.Node} ref_node Reference node to set node before/after.
8571 		 * @param {Boolean} before Optional state to insert the node before the reference node.
8572 		 * @return {tinymce.html.Node} The node that got inserted.
8573 		 */
8574 		insert: function(node, ref_node, before) {
8575 			var parent;
8576 
8577 			if (node.parent) {
8578 				node.remove();
8579 			}
8580 
8581 			parent = ref_node.parent || this;
8582 
8583 			if (before) {
8584 				if (ref_node === parent.firstChild) {
8585 					parent.firstChild = node;
8586 				} else {
8587 					ref_node.prev.next = node;
8588 				}
8589 
8590 				node.prev = ref_node.prev;
8591 				node.next = ref_node;
8592 				ref_node.prev = node;
8593 			} else {
8594 				if (ref_node === parent.lastChild) {
8595 					parent.lastChild = node;
8596 				} else {
8597 					ref_node.next.prev = node;
8598 				}
8599 
8600 				node.next = ref_node.next;
8601 				node.prev = ref_node;
8602 				ref_node.next = node;
8603 			}
8604 
8605 			node.parent = parent;
8606 
8607 			return node;
8608 		},
8609 
8610 		/**
8611 		 * Get all children by name.
8612 		 *
8613 		 * @method getAll
8614 		 * @param {String} name Name of the child nodes to collect.
8615 		 * @return {Array} Array with child nodes matchin the specified name.
8616 		 */
8617 		getAll: function(name) {
8618 			var self = this, node, collection = [];
8619 
8620 			for (node = self.firstChild; node; node = walk(node, self)) {
8621 				if (node.name === name) {
8622 					collection.push(node);
8623 				}
8624 			}
8625 
8626 			return collection;
8627 		},
8628 
8629 		/**
8630 		 * Removes all children of the current node.
8631 		 *
8632 		 * @method empty
8633 		 * @return {tinymce.html.Node} The current node that got cleared.
8634 		 */
8635 		empty: function() {
8636 			var self = this, nodes, i, node;
8637 
8638 			// Remove all children
8639 			if (self.firstChild) {
8640 				nodes = [];
8641 
8642 				// Collect the children
8643 				for (node = self.firstChild; node; node = walk(node, self)) {
8644 					nodes.push(node);
8645 				}
8646 
8647 				// Remove the children
8648 				i = nodes.length;
8649 				while (i--) {
8650 					node = nodes[i];
8651 					node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
8652 				}
8653 			}
8654 
8655 			self.firstChild = self.lastChild = null;
8656 
8657 			return self;
8658 		},
8659 
8660 		/**
8661 		 * Returns true/false if the node is to be considered empty or not.
8662 		 *
8663 		 * @example
8664 		 * node.isEmpty({img: true});
8665 		 * @method isEmpty
8666 		 * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements.
8667 		 * @return {Boolean} true/false if the node is empty or not.
8668 		 */
8669 		isEmpty: function(elements) {
8670 			var self = this, node = self.firstChild, i, name;
8671 
8672 			if (node) {
8673 				do {
8674 					if (node.type === 1) {
8675 						// Ignore bogus elements
8676 						if (node.attributes.map['data-mce-bogus']) {
8677 							continue;
8678 						}
8679 
8680 						// Keep empty elements like <img />
8681 						if (elements[node.name]) {
8682 							return false;
8683 						}
8684 
8685 						// Keep elements with data attributes or name attribute like <a name="1"></a>
8686 						i = node.attributes.length;
8687 						while (i--) {
8688 							name = node.attributes[i].name;
8689 							if (name === "name" || name.indexOf('data-mce-') === 0) {
8690 								return false;
8691 							}
8692 						}
8693 					}
8694 
8695 					// Keep comments
8696 					if (node.type === 8) {
8697 						return false;
8698 					}
8699 
8700 					// Keep non whitespace text nodes
8701 					if ((node.type === 3 && !whiteSpaceRegExp.test(node.value))) {
8702 						return false;
8703 					}
8704 				} while ((node = walk(node, self)));
8705 			}
8706 
8707 			return true;
8708 		},
8709 
8710 		/**
8711 		 * Walks to the next or previous node and returns that node or null if it wasn't found.
8712 		 *
8713 		 * @method walk
8714 		 * @param {Boolean} prev Optional previous node state defaults to false.
8715 		 * @return {tinymce.html.Node} Node that is next to or previous of the current node.
8716 		 */
8717 		walk: function(prev) {
8718 			return walk(this, null, prev);
8719 		}
8720 	};
8721 
8722 	/**
8723 	 * Creates a node of a specific type.
8724 	 *
8725 	 * @static
8726 	 * @method create
8727 	 * @param {String} name Name of the node type to create for example "b" or "#text".
8728 	 * @param {Object} attrs Name/value collection of attributes that will be applied to elements.
8729 	 */
8730 	Node.create = function(name, attrs) {
8731 		var node, attrName;
8732 
8733 		// Create node
8734 		node = new Node(name, typeLookup[name] || 1);
8735 
8736 		// Add attributes if needed
8737 		if (attrs) {
8738 			for (attrName in attrs) {
8739 				node.attr(attrName, attrs[attrName]);
8740 			}
8741 		}
8742 
8743 		return node;
8744 	};
8745 
8746 	return Node;
8747 });
8748 
8749 // Included from: js/tinymce/classes/html/Schema.js
8750 
8751 /**
8752  * Schema.js
8753  *
8754  * Copyright, Moxiecode Systems AB
8755  * Released under LGPL License.
8756  *
8757  * License: http://www.tinymce.com/license
8758  * Contributing: http://www.tinymce.com/contributing
8759  */
8760 
8761 /**
8762  * Schema validator class.
8763  *
8764  * @class tinymce.html.Schema
8765  * @example
8766  *  if (tinymce.activeEditor.schema.isValidChild('p', 'span'))
8767  *    alert('span is valid child of p.');
8768  *
8769  *  if (tinymce.activeEditor.schema.getElementRule('p'))
8770  *    alert('P is a valid element.');
8771  *
8772  * @class tinymce.html.Schema
8773  * @version 3.4
8774  */
8775 define("tinymce/html/Schema", [
8776 	"tinymce/util/Tools"
8777 ], function(Tools) {
8778 	var mapCache = {};
8779 	var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
8780 
8781 	function split(items, delim) {
8782 		return items ? items.split(delim || ' ') : [];
8783 	}
8784 
8785 	/**
8786 	 * Builds a schema lookup table
8787 	 *
8788 	 * @private
8789 	 * @param {String} type html4, html5 or html5-strict schema type.
8790 	 * @return {Object} Schema lookup table.
8791 	 */
8792 	function compileSchema(type) {
8793 		var schema = {}, globalAttributes, blockContent;
8794 		var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent;
8795 
8796 		function add(name, attributes, children) {
8797 			var ni, i, attributesOrder, args = arguments;
8798 
8799 			function arrayToMap(array) {
8800 				var map = {}, i, l;
8801 
8802 				for (i = 0, l = array.length; i < l; i++) {
8803 					map[array[i]] = {};
8804 				}
8805 
8806 				return map;
8807 			}
8808 
8809 			children = children || [];
8810 			attributes = attributes || "";
8811 
8812 			if (typeof(children) === "string") {
8813 				children = split(children);
8814 			}
8815 
8816 			// Split string children
8817 			for (i = 3; i < args.length; i++) {
8818 				if (typeof(args[i]) === "string") {
8819 					args[i] = split(args[i]);
8820 				}
8821 
8822 				children.push.apply(children, args[i]);
8823 			}
8824 
8825 			name = split(name);
8826 			ni = name.length;
8827 			while (ni--) {
8828 				attributesOrder = [].concat(globalAttributes, split(attributes));
8829 				schema[name[ni]] = {
8830 					attributes: arrayToMap(attributesOrder),
8831 					attributesOrder: attributesOrder,
8832 					children: arrayToMap(children)
8833 				};
8834 			}
8835 		}
8836 
8837 		function addAttrs(name, attributes) {
8838 			var ni, schemaItem, i, l;
8839 
8840 			name = split(name);
8841 			ni = name.length;
8842 			attributes = split(attributes);
8843 			while (ni--) {
8844 				schemaItem = schema[name[ni]];
8845 				for (i = 0, l = attributes.length; i < l; i++) {
8846 					schemaItem.attributes[attributes[i]] = {};
8847 					schemaItem.attributesOrder.push(attributes[i]);
8848 				}
8849 			}
8850 		}
8851 
8852 		// Use cached schema
8853 		if (mapCache[type]) {
8854 			return mapCache[type];
8855 		}
8856 
8857 		// Attributes present on all elements
8858 		globalAttributes = split("id accesskey class dir lang style tabindex title");
8859 
8860 		// Event attributes can be opt-in/opt-out
8861 		/*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " +
8862 				"ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " +
8863 				"onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " +
8864 				"onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " +
8865 				"onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " +
8866 				"onwaiting"
8867 		);*/
8868 
8869 		// Block content elements
8870 		blockContent = split(
8871 			"address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"
8872 		);
8873 
8874 		// Phrasing content elements from the HTML5 spec (inline)
8875 		phrasingContent = split(
8876 			"a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " +
8877 			"label map noscript object q s samp script select small span strong sub sup " +
8878 			"textarea u var #text #comment"
8879 		);
8880 
8881 		// Add HTML5 items to globalAttributes, blockContent, phrasingContent
8882 		if (type != "html4") {
8883 			globalAttributes.push.apply(globalAttributes, split("contenteditable contextmenu draggable dropzone " +
8884 				"hidden spellcheck translate"));
8885 			blockContent.push.apply(blockContent, split("article aside details dialog figure header footer hgroup section nav"));
8886 			phrasingContent.push.apply(phrasingContent, split("audio canvas command datalist mark meter output progress time wbr " +
8887 				"video ruby bdi keygen"));
8888 		}
8889 
8890 		// Add HTML4 elements unless it's html5-strict
8891 		if (type != "html5-strict") {
8892 			globalAttributes.push("xml:lang");
8893 
8894 			html4PhrasingContent = split("acronym applet basefont big font strike tt");
8895 			phrasingContent.push.apply(phrasingContent, html4PhrasingContent);
8896 
8897 			each(html4PhrasingContent, function(name) {
8898 				add(name, "", phrasingContent);
8899 			});
8900 
8901 			html4BlockContent = split("center dir isindex noframes");
8902 			blockContent.push.apply(blockContent, html4BlockContent);
8903 
8904 			// Flow content elements from the HTML5 spec (block+inline)
8905 			flowContent = [].concat(blockContent, phrasingContent);
8906 
8907 			each(html4BlockContent, function(name) {
8908 				add(name, "", flowContent);
8909 			});
8910 		}
8911 
8912 		// Flow content elements from the HTML5 spec (block+inline)
8913 		flowContent = flowContent || [].concat(blockContent, phrasingContent);
8914 
8915 		// HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement
8916 		// Schema items <element name>, <specific attributes>, <children ..>
8917 		add("html", "manifest", "head body");
8918 		add("head", "", "base command link meta noscript script style title");
8919 		add("title hr noscript br");
8920 		add("base", "href target");
8921 		add("link", "href rel media hreflang type sizes hreflang");
8922 		add("meta", "name http-equiv content charset");
8923 		add("style", "media type scoped");
8924 		add("script", "src async defer type charset");
8925 		add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " +
8926 				"onhashchange onload onmessage onoffline ononline onpagehide onpageshow " +
8927 				"onpopstate onresize onscroll onstorage onunload", flowContent);
8928 		add("address dt dd div caption", "", flowContent);
8929 		add("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn", "", phrasingContent);
8930 		add("blockquote", "cite", flowContent);
8931 		add("ol", "reversed start type", "li");
8932 		add("ul", "", "li");
8933 		add("li", "value", flowContent);
8934 		add("dl", "", "dt dd");
8935 		add("a", "href target rel media hreflang type", phrasingContent);
8936 		add("q", "cite", phrasingContent);
8937 		add("ins del", "cite datetime", flowContent);
8938 		add("img", "src alt usemap ismap width height");
8939 		add("iframe", "src name width height", flowContent);
8940 		add("embed", "src type width height");
8941 		add("object", "data type typemustmatch name usemap form width height", flowContent, "param");
8942 		add("param", "name value");
8943 		add("map", "name", flowContent, "area");
8944 		add("area", "alt coords shape href target rel media hreflang type");
8945 		add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : ""));
8946 		add("colgroup", "span", "col");
8947 		add("col", "span");
8948 		add("tbody thead tfoot", "", "tr");
8949 		add("tr", "", "td th");
8950 		add("td", "colspan rowspan headers", flowContent);
8951 		add("th", "colspan rowspan headers scope abbr", flowContent);
8952 		add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent);
8953 		add("fieldset", "disabled form name", flowContent, "legend");
8954 		add("label", "form for", phrasingContent);
8955 		add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " +
8956 				"formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"
8957 		);
8958 		add("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value",
8959 			type == "html4" ? flowContent : phrasingContent);
8960 		add("select", "disabled form multiple name required size", "option optgroup");
8961 		add("optgroup", "disabled label", "option");
8962 		add("option", "disabled label selected value");
8963 		add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap");
8964 		add("menu", "type label", flowContent, "li");
8965 		add("noscript", "", flowContent);
8966 
8967 		// Extend with HTML5 elements
8968 		if (type != "html4") {
8969 			add("wbr");
8970 			add("ruby", "", phrasingContent, "rt rp");
8971 			add("figcaption", "", flowContent);
8972 			add("mark rt rp summary bdi", "", phrasingContent);
8973 			add("canvas", "width height", flowContent);
8974 			add("video", "src crossorigin poster preload autoplay mediagroup loop " +
8975 				"muted controls width height buffered", flowContent, "track source");
8976 			add("audio", "src crossorigin preload autoplay mediagroup loop muted controls buffered volume", flowContent, "track source");
8977 			add("source", "src type media");
8978 			add("track", "kind src srclang label default");
8979 			add("datalist", "", phrasingContent, "option");
8980 			add("article section nav aside header footer", "", flowContent);
8981 			add("hgroup", "", "h1 h2 h3 h4 h5 h6");
8982 			add("figure", "", flowContent, "figcaption");
8983 			add("time", "datetime", phrasingContent);
8984 			add("dialog", "open", flowContent);
8985 			add("command", "type label icon disabled checked radiogroup command");
8986 			add("output", "for form name", phrasingContent);
8987 			add("progress", "value max", phrasingContent);
8988 			add("meter", "value min max low high optimum", phrasingContent);
8989 			add("details", "open", flowContent, "summary");
8990 			add("keygen", "autofocus challenge disabled form keytype name");
8991 		}
8992 
8993 		// Extend with HTML4 attributes unless it's html5-strict
8994 		if (type != "html5-strict") {
8995 			addAttrs("script", "language xml:space");
8996 			addAttrs("style", "xml:space");
8997 			addAttrs("object", "declare classid codebase codetype archive standby align border hspace vspace");
8998 			addAttrs("param", "valuetype type");
8999 			addAttrs("a", "charset name rev shape coords");
9000 			addAttrs("br", "clear");
9001 			addAttrs("applet", "codebase archive code object alt name width height align hspace vspace");
9002 			addAttrs("img", "name longdesc align border hspace vspace");
9003 			addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align");
9004 			addAttrs("font basefont", "size color face");
9005 			addAttrs("input", "usemap align");
9006 			addAttrs("select", "onchange");
9007 			addAttrs("textarea");
9008 			addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align");
9009 			addAttrs("ul", "type compact");
9010 			addAttrs("li", "type");
9011 			addAttrs("ol dl menu dir", "compact");
9012 			addAttrs("pre", "width xml:space");
9013 			addAttrs("hr", "align noshade size width");
9014 			addAttrs("isindex", "prompt");
9015 			addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor");
9016 			addAttrs("col", "width align char charoff valign");
9017 			addAttrs("colgroup", "width align char charoff valign");
9018 			addAttrs("thead", "align char charoff valign");
9019 			addAttrs("tr", "align char charoff valign bgcolor");
9020 			addAttrs("th", "axis align char charoff valign nowrap bgcolor width height");
9021 			addAttrs("form", "accept");
9022 			addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height");
9023 			addAttrs("tfoot", "align char charoff valign");
9024 			addAttrs("tbody", "align char charoff valign");
9025 			addAttrs("area", "nohref");
9026 			addAttrs("body", "background bgcolor text link vlink alink");
9027 		}
9028 
9029 		// Extend with HTML5 attributes unless it's html4
9030 		if (type != "html4") {
9031 			addAttrs("input button select textarea", "autofocus");
9032 			addAttrs("input textarea", "placeholder");
9033 			addAttrs("a", "download");
9034 			addAttrs("link script img", "crossorigin");
9035 			addAttrs("iframe", "sandbox seamless allowfullscreen"); // Excluded: srcdoc
9036 		}
9037 
9038 		// Special: iframe, ruby, video, audio, label
9039 
9040 		// Delete children of the same name from it's parent
9041 		// For example: form can't have a child of the name form
9042 		each(split('a form meter progress dfn'), function(name) {
9043 			if (schema[name]) {
9044 				delete schema[name].children[name];
9045 			}
9046 		});
9047 
9048 		// Delete header, footer, sectioning and heading content descendants
9049 		/*each('dt th address', function(name) {
9050 			delete schema[name].children[name];
9051 		});*/
9052 
9053 		// Caption can't have tables
9054 		delete schema.caption.children.table;
9055 
9056 		// TODO: LI:s can only have value if parent is OL
9057 
9058 		// TODO: Handle transparent elements
9059 		// a ins del canvas map
9060 
9061 		mapCache[type] = schema;
9062 
9063 		return schema;
9064 	}
9065 
9066 	/**
9067 	 * Constructs a new Schema instance.
9068 	 *
9069 	 * @constructor
9070 	 * @method Schema
9071 	 * @param {Object} settings Name/value settings object.
9072 	 */
9073 	return function(settings) {
9074 		var self = this, elements = {}, children = {}, patternElements = [], validStyles, schemaItems;
9075 		var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap;
9076 		var blockElementsMap, nonEmptyElementsMap, textBlockElementsMap, customElementsMap = {}, specialElements = {};
9077 
9078 		// Creates an lookup table map object for the specified option or the default value
9079 		function createLookupTable(option, default_value, extendWith) {
9080 			var value = settings[option];
9081 
9082 			if (!value) {
9083 				// Get cached default map or make it if needed
9084 				value = mapCache[option];
9085 
9086 				if (!value) {
9087 					value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));
9088 					value = extend(value, extendWith);
9089 
9090 					mapCache[option] = value;
9091 				}
9092 			} else {
9093 				// Create custom map
9094 				value = makeMap(value, /[, ]/, makeMap(value.toUpperCase(), /[, ]/));
9095 			}
9096 
9097 			return value;
9098 		}
9099 
9100 		settings = settings || {};
9101 		schemaItems = compileSchema(settings.schema);
9102 
9103 		// Allow all elements and attributes if verify_html is set to false
9104 		if (settings.verify_html === false) {
9105 			settings.valid_elements = '*[*]';
9106 		}
9107 
9108 		// Build styles list
9109 		if (settings.valid_styles) {
9110 			validStyles = {};
9111 
9112 			// Convert styles into a rule list
9113 			each(settings.valid_styles, function(value, key) {
9114 				validStyles[key] = explode(value);
9115 			});
9116 		}
9117 
9118 		// Setup map objects
9119 		whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object');
9120 		selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
9121 		shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' +
9122 			'meta param embed source wbr track');
9123 		boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' +
9124 			'noshade nowrap readonly selected autoplay loop controls');
9125 		nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script', shortEndedElementsMap);
9126 		textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' +
9127 						'blockquote center dir fieldset header footer article section hgroup aside nav figure');
9128 		blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
9129 						'th tr td li ol ul caption dl dt dd noscript menu isindex option ' +
9130 						'datalist select optgroup', textBlockElementsMap);
9131 
9132 		each((settings.special || 'script noscript style textarea').split(' '), function(name) {
9133 			specialElements[name] = new RegExp('<\/' + name + '[^>]*>','gi');
9134 		});
9135 
9136 		// Converts a wildcard expression string to a regexp for example *a will become /.*a/.
9137 		function patternToRegExp(str) {
9138 			return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
9139 		}
9140 
9141 		// Parses the specified valid_elements string and adds to the current rules
9142 		// This function is a bit hard to read since it's heavily optimized for speed
9143 		function addValidElements(valid_elements) {
9144 			var ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
9145 				prefix, outputName, globalAttributes, globalAttributesOrder, key, value,
9146 				elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,
9147 				attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
9148 				hasPatternsRegExp = /[*?+]/;
9149 
9150 			if (valid_elements) {
9151 				// Split valid elements into an array with rules
9152 				valid_elements = split(valid_elements, ',');
9153 
9154 				if (elements['@']) {
9155 					globalAttributes = elements['@'].attributes;
9156 					globalAttributesOrder = elements['@'].attributesOrder;
9157 				}
9158 
9159 				// Loop all rules
9160 				for (ei = 0, el = valid_elements.length; ei < el; ei++) {
9161 					// Parse element rule
9162 					matches = elementRuleRegExp.exec(valid_elements[ei]);
9163 					if (matches) {
9164 						// Setup local names for matches
9165 						prefix = matches[1];
9166 						elementName = matches[2];
9167 						outputName = matches[3];
9168 						attrData = matches[5];
9169 
9170 						// Create new attributes and attributesOrder
9171 						attributes = {};
9172 						attributesOrder = [];
9173 
9174 						// Create the new element
9175 						element = {
9176 							attributes: attributes,
9177 							attributesOrder: attributesOrder
9178 						};
9179 
9180 						// Padd empty elements prefix
9181 						if (prefix === '#') {
9182 							element.paddEmpty = true;
9183 						}
9184 
9185 						// Remove empty elements prefix
9186 						if (prefix === '-') {
9187 							element.removeEmpty = true;
9188 						}
9189 
9190 						if (matches[4] === '!') {
9191 							element.removeEmptyAttrs = true;
9192 						}
9193 
9194 						// Copy attributes from global rule into current rule
9195 						if (globalAttributes) {
9196 							for (key in globalAttributes) {
9197 								attributes[key] = globalAttributes[key];
9198 							}
9199 
9200 							attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
9201 						}
9202 
9203 						// Attributes defined
9204 						if (attrData) {
9205 							attrData = split(attrData, '|');
9206 							for (ai = 0, al = attrData.length; ai < al; ai++) {
9207 								matches = attrRuleRegExp.exec(attrData[ai]);
9208 								if (matches) {
9209 									attr = {};
9210 									attrType = matches[1];
9211 									attrName = matches[2].replace(/::/g, ':');
9212 									prefix = matches[3];
9213 									value = matches[4];
9214 
9215 									// Required
9216 									if (attrType === '!') {
9217 										element.attributesRequired = element.attributesRequired || [];
9218 										element.attributesRequired.push(attrName);
9219 										attr.required = true;
9220 									}
9221 
9222 									// Denied from global
9223 									if (attrType === '-') {
9224 										delete attributes[attrName];
9225 										attributesOrder.splice(inArray(attributesOrder, attrName), 1);
9226 										continue;
9227 									}
9228 
9229 									// Default value
9230 									if (prefix) {
9231 										// Default value
9232 										if (prefix === '=') {
9233 											element.attributesDefault = element.attributesDefault || [];
9234 											element.attributesDefault.push({name: attrName, value: value});
9235 											attr.defaultValue = value;
9236 										}
9237 
9238 										// Forced value
9239 										if (prefix === ':') {
9240 											element.attributesForced = element.attributesForced || [];
9241 											element.attributesForced.push({name: attrName, value: value});
9242 											attr.forcedValue = value;
9243 										}
9244 
9245 										// Required values
9246 										if (prefix === '<') {
9247 											attr.validValues = makeMap(value, '?');
9248 										}
9249 									}
9250 
9251 									// Check for attribute patterns
9252 									if (hasPatternsRegExp.test(attrName)) {
9253 										element.attributePatterns = element.attributePatterns || [];
9254 										attr.pattern = patternToRegExp(attrName);
9255 										element.attributePatterns.push(attr);
9256 									} else {
9257 										// Add attribute to order list if it doesn't already exist
9258 										if (!attributes[attrName]) {
9259 											attributesOrder.push(attrName);
9260 										}
9261 
9262 										attributes[attrName] = attr;
9263 									}
9264 								}
9265 							}
9266 						}
9267 
9268 						// Global rule, store away these for later usage
9269 						if (!globalAttributes && elementName == '@') {
9270 							globalAttributes = attributes;
9271 							globalAttributesOrder = attributesOrder;
9272 						}
9273 
9274 						// Handle substitute elements such as b/strong
9275 						if (outputName) {
9276 							element.outputName = elementName;
9277 							elements[outputName] = element;
9278 						}
9279 
9280 						// Add pattern or exact element
9281 						if (hasPatternsRegExp.test(elementName)) {
9282 							element.pattern = patternToRegExp(elementName);
9283 							patternElements.push(element);
9284 						} else {
9285 							elements[elementName] = element;
9286 						}
9287 					}
9288 				}
9289 			}
9290 		}
9291 
9292 		function setValidElements(valid_elements) {
9293 			elements = {};
9294 			patternElements = [];
9295 
9296 			addValidElements(valid_elements);
9297 
9298 			each(schemaItems, function(element, name) {
9299 				children[name] = element.children;
9300 			});
9301 		}
9302 
9303 		// Adds custom non HTML elements to the schema
9304 		function addCustomElements(custom_elements) {
9305 			var customElementRegExp = /^(~)?(.+)$/;
9306 
9307 			if (custom_elements) {
9308 				// Flush cached items since we are altering the default maps
9309 				mapCache.text_block_elements = mapCache.block_elements = null;
9310 
9311 				each(split(custom_elements, ','), function(rule) {
9312 					var matches = customElementRegExp.exec(rule),
9313 						inline = matches[1] === '~',
9314 						cloneName = inline ? 'span' : 'div',
9315 						name = matches[2];
9316 
9317 					children[name] = children[cloneName];
9318 					customElementsMap[name] = cloneName;
9319 
9320 					// If it's not marked as inline then add it to valid block elements
9321 					if (!inline) {
9322 						blockElementsMap[name.toUpperCase()] = {};
9323 						blockElementsMap[name] = {};
9324 					}
9325 
9326 					// Add elements clone if needed
9327 					if (!elements[name]) {
9328 						var customRule = elements[cloneName];
9329 
9330 						customRule = extend({}, customRule);
9331 						delete customRule.removeEmptyAttrs;
9332 						delete customRule.removeEmpty;
9333 
9334 						elements[name] = customRule;
9335 					}
9336 
9337 					// Add custom elements at span/div positions
9338 					each(children, function(element, elmName) {
9339 						if (element[cloneName]) {
9340 							children[elmName] = element = extend({}, children[elmName]);
9341 							element[name] = element[cloneName];
9342 						}
9343 					});
9344 				});
9345 			}
9346 		}
9347 
9348 		// Adds valid children to the schema object
9349 		function addValidChildren(valid_children) {
9350 			var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
9351 
9352 			if (valid_children) {
9353 				each(split(valid_children, ','), function(rule) {
9354 					var matches = childRuleRegExp.exec(rule), parent, prefix;
9355 
9356 					if (matches) {
9357 						prefix = matches[1];
9358 
9359 						// Add/remove items from default
9360 						if (prefix) {
9361 							parent = children[matches[2]];
9362 						} else {
9363 							parent = children[matches[2]] = {'#comment': {}};
9364 						}
9365 
9366 						parent = children[matches[2]];
9367 
9368 						each(split(matches[3], '|'), function(child) {
9369 							if (prefix === '-') {
9370 								// Clone the element before we delete
9371 								// things in it to not mess up default schemas
9372 								children[matches[2]] = parent = extend({}, children[matches[2]]);
9373 
9374 								delete parent[child];
9375 							} else {
9376 								parent[child] = {};
9377 							}
9378 						});
9379 					}
9380 				});
9381 			}
9382 		}
9383 
9384 		function getElementRule(name) {
9385 			var element = elements[name], i;
9386 
9387 			// Exact match found
9388 			if (element) {
9389 				return element;
9390 			}
9391 
9392 			// No exact match then try the patterns
9393 			i = patternElements.length;
9394 			while (i--) {
9395 				element = patternElements[i];
9396 
9397 				if (element.pattern.test(name)) {
9398 					return element;
9399 				}
9400 			}
9401 		}
9402 
9403 		if (!settings.valid_elements) {
9404 			// No valid elements defined then clone the elements from the schema spec
9405 			each(schemaItems, function(element, name) {
9406 				elements[name] = {
9407 					attributes: element.attributes,
9408 					attributesOrder: element.attributesOrder
9409 				};
9410 
9411 				children[name] = element.children;
9412 			});
9413 
9414 			// Switch these on HTML4
9415 			if (settings.schema != "html5") {
9416 				each(split('strong/b em/i'), function(item) {
9417 					item = split(item, '/');
9418 					elements[item[1]].outputName = item[0];
9419 				});
9420 			}
9421 
9422 			// Add default alt attribute for images
9423 			elements.img.attributesDefault = [{name: 'alt', value: ''}];
9424 
9425 			// Remove these if they are empty by default
9426 			each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function(name) {
9427 				if (elements[name]) {
9428 					elements[name].removeEmpty = true;
9429 				}
9430 			});
9431 
9432 			// Padd these by default
9433 			each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption'), function(name) {
9434 				elements[name].paddEmpty = true;
9435 			});
9436 
9437 			// Remove these if they have no attributes
9438 			each(split('span'), function(name) {
9439 				elements[name].removeEmptyAttrs = true;
9440 			});
9441 
9442 			// Remove these by default
9443 			// TODO: Reenable in 4.1
9444 			/*each(split('script style'), function(name) {
9445 				delete elements[name];
9446 			});*/
9447 		} else {
9448 			setValidElements(settings.valid_elements);
9449 		}
9450 
9451 		addCustomElements(settings.custom_elements);
9452 		addValidChildren(settings.valid_children);
9453 		addValidElements(settings.extended_valid_elements);
9454 
9455 		// Todo: Remove this when we fix list handling to be valid
9456 		addValidChildren('+ol[ul|ol],+ul[ul|ol]');
9457 
9458 		// Delete invalid elements
9459 		if (settings.invalid_elements) {
9460 			each(explode(settings.invalid_elements), function(item) {
9461 				if (elements[item]) {
9462 					delete elements[item];
9463 				}
9464 			});
9465 		}
9466 
9467 		// If the user didn't allow span only allow internal spans
9468 		if (!getElementRule('span')) {
9469 			addValidElements('span[!data-mce-type|*]');
9470 		}
9471 
9472 		/**
9473 		 * Name/value map object with valid parents and children to those parents.
9474 		 *
9475 		 * @example
9476 		 * children = {
9477 		 *    div:{p:{}, h1:{}}
9478 		 * };
9479 		 * @field children
9480 		 * @type Object
9481 		 */
9482 		self.children = children;
9483 
9484 		/**
9485 		 * Name/value map object with valid styles for each element.
9486 		 *
9487 		 * @field styles
9488 		 * @type Object
9489 		 */
9490 		self.styles = validStyles;
9491 
9492 		/**
9493 		 * Returns a map with boolean attributes.
9494 		 *
9495 		 * @method getBoolAttrs
9496 		 * @return {Object} Name/value lookup map for boolean attributes.
9497 		 */
9498 		self.getBoolAttrs = function() {
9499 			return boolAttrMap;
9500 		};
9501 
9502 		/**
9503 		 * Returns a map with block elements.
9504 		 *
9505 		 * @method getBlockElements
9506 		 * @return {Object} Name/value lookup map for block elements.
9507 		 */
9508 		self.getBlockElements = function() {
9509 			return blockElementsMap;
9510 		};
9511 
9512 		/**
9513 		 * Returns a map with text block elements. Such as: p,h1-h6,div,address
9514 		 *
9515 		 * @method getTextBlockElements
9516 		 * @return {Object} Name/value lookup map for block elements.
9517 		 */
9518 		self.getTextBlockElements = function() {
9519 			return textBlockElementsMap;
9520 		};
9521 
9522 		/**
9523 		 * Returns a map with short ended elements such as BR or IMG.
9524 		 *
9525 		 * @method getShortEndedElements
9526 		 * @return {Object} Name/value lookup map for short ended elements.
9527 		 */
9528 		self.getShortEndedElements = function() {
9529 			return shortEndedElementsMap;
9530 		};
9531 
9532 		/**
9533 		 * Returns a map with self closing tags such as <li>.
9534 		 *
9535 		 * @method getSelfClosingElements
9536 		 * @return {Object} Name/value lookup map for self closing tags elements.
9537 		 */
9538 		self.getSelfClosingElements = function() {
9539 			return selfClosingElementsMap;
9540 		};
9541 
9542 		/**
9543 		 * Returns a map with elements that should be treated as contents regardless if it has text
9544 		 * content in them or not such as TD, VIDEO or IMG.
9545 		 *
9546 		 * @method getNonEmptyElements
9547 		 * @return {Object} Name/value lookup map for non empty elements.
9548 		 */
9549 		self.getNonEmptyElements = function() {
9550 			return nonEmptyElementsMap;
9551 		};
9552 
9553 		/**
9554 		 * Returns a map with elements where white space is to be preserved like PRE or SCRIPT.
9555 		 *
9556 		 * @method getWhiteSpaceElements
9557 		 * @return {Object} Name/value lookup map for white space elements.
9558 		 */
9559 		self.getWhiteSpaceElements = function() {
9560 			return whiteSpaceElementsMap;
9561 		};
9562 
9563 		/**
9564 		 * Returns a map with special elements. These are elements that needs to be parsed
9565 		 * in a special way such as script, style, textarea etc. The map object values
9566 		 * are regexps used to find the end of the element.
9567 		 *
9568 		 * @method getSpecialElements
9569 		 * @return {Object} Name/value lookup map for special elements.
9570 		 */
9571 		self.getSpecialElements = function() {
9572 			return specialElements;
9573 		};
9574 
9575 		/**
9576 		 * Returns true/false if the specified element and it's child is valid or not
9577 		 * according to the schema.
9578 		 *
9579 		 * @method isValidChild
9580 		 * @param {String} name Element name to check for.
9581 		 * @param {String} child Element child to verify.
9582 		 * @return {Boolean} True/false if the element is a valid child of the specified parent.
9583 		 */
9584 		self.isValidChild = function(name, child) {
9585 			var parent = children[name];
9586 
9587 			return !!(parent && parent[child]);
9588 		};
9589 
9590 		/**
9591 		 * Returns true/false if the specified element name and optional attribute is
9592 		 * valid according to the schema.
9593 		 *
9594 		 * @method isValid
9595 		 * @param {String} name Name of element to check.
9596 		 * @param {String} attr Optional attribute name to check for.
9597 		 * @return {Boolean} True/false if the element and attribute is valid.
9598 		 */
9599 		self.isValid = function(name, attr) {
9600 			var attrPatterns, i, rule = getElementRule(name);
9601 
9602 			// Check if it's a valid element
9603 			if (rule) {
9604 				if (attr) {
9605 					// Check if attribute name exists
9606 					if (rule.attributes[attr]) {
9607 						return true;
9608 					}
9609 
9610 					// Check if attribute matches a regexp pattern
9611 					attrPatterns = rule.attributePatterns;
9612 					if (attrPatterns) {
9613 						i = attrPatterns.length;
9614 						while (i--) {
9615 							if (attrPatterns[i].pattern.test(name)) {
9616 								return true;
9617 							}
9618 						}
9619 					}
9620 				} else {
9621 					return true;
9622 				}
9623 			}
9624 
9625 			// No match
9626 			return false;
9627 		};
9628 
9629 		/**
9630 		 * Returns true/false if the specified element is valid or not
9631 		 * according to the schema.
9632 		 *
9633 		 * @method getElementRule
9634 		 * @param {String} name Element name to check for.
9635 		 * @return {Object} Element object or undefined if the element isn't valid.
9636 		 */
9637 		self.getElementRule = getElementRule;
9638 
9639 		/**
9640 		 * Returns an map object of all custom elements.
9641 		 *
9642 		 * @method getCustomElements
9643 		 * @return {Object} Name/value map object of all custom elements.
9644 		 */
9645 		self.getCustomElements = function() {
9646 			return customElementsMap;
9647 		};
9648 
9649 		/**
9650 		 * Parses a valid elements string and adds it to the schema. The valid elements
9651 		 * format is for example "element[attr=default|otherattr]".
9652 		 * Existing rules will be replaced with the ones specified, so this extends the schema.
9653 		 *
9654 		 * @method addValidElements
9655 		 * @param {String} valid_elements String in the valid elements format to be parsed.
9656 		 */
9657 		self.addValidElements = addValidElements;
9658 
9659 		/**
9660 		 * Parses a valid elements string and sets it to the schema. The valid elements
9661 		 * format is for example "element[attr=default|otherattr]".
9662 		 * Existing rules will be replaced with the ones specified, so this extends the schema.
9663 		 *
9664 		 * @method setValidElements
9665 		 * @param {String} valid_elements String in the valid elements format to be parsed.
9666 		 */
9667 		self.setValidElements = setValidElements;
9668 
9669 		/**
9670 		 * Adds custom non HTML elements to the schema.
9671 		 *
9672 		 * @method addCustomElements
9673 		 * @param {String} custom_elements Comma separated list of custom elements to add.
9674 		 */
9675 		self.addCustomElements = addCustomElements;
9676 
9677 		/**
9678 		 * Parses a valid children string and adds them to the schema structure. The valid children
9679 		 * format is for example: "element[child1|child2]".
9680 		 *
9681 		 * @method addValidChildren
9682 		 * @param {String} valid_children Valid children elements string to parse
9683 		 */
9684 		self.addValidChildren = addValidChildren;
9685 
9686 		self.elements = elements;
9687 	};
9688 });
9689 
9690 // Included from: js/tinymce/classes/html/SaxParser.js
9691 
9692 /**
9693  * SaxParser.js
9694  *
9695  * Copyright, Moxiecode Systems AB
9696  * Released under LGPL License.
9697  *
9698  * License: http://www.tinymce.com/license
9699  * Contributing: http://www.tinymce.com/contributing
9700  */
9701 
9702 /*eslint max-depth:[2, 9] */
9703 
9704 /**
9705  * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will
9706  * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements
9707  * and attributes that doesn't fit the schema if the validate setting is enabled.
9708  *
9709  * @example
9710  * var parser = new tinymce.html.SaxParser({
9711  *     validate: true,
9712  *
9713  *     comment: function(text) {
9714  *         console.log('Comment:', text);
9715  *     },
9716  *
9717  *     cdata: function(text) {
9718  *         console.log('CDATA:', text);
9719  *     },
9720  *
9721  *     text: function(text, raw) {
9722  *         console.log('Text:', text, 'Raw:', raw);
9723  *     },
9724  *
9725  *     start: function(name, attrs, empty) {
9726  *         console.log('Start:', name, attrs, empty);
9727  *     },
9728  *
9729  *     end: function(name) {
9730  *         console.log('End:', name);
9731  *     },
9732  *
9733  *     pi: function(name, text) {
9734  *         console.log('PI:', name, text);
9735  *     },
9736  *
9737  *     doctype: function(text) {
9738  *         console.log('DocType:', text);
9739  *     }
9740  * }, schema);
9741  * @class tinymce.html.SaxParser
9742  * @version 3.4
9743  */
9744 define("tinymce/html/SaxParser", [
9745 	"tinymce/html/Schema",
9746 	"tinymce/html/Entities",
9747 	"tinymce/util/Tools"
9748 ], function(Schema, Entities, Tools) {
9749 	var each = Tools.each;
9750 
9751 	/**
9752 	 * Constructs a new SaxParser instance.
9753 	 *
9754 	 * @constructor
9755 	 * @method SaxParser
9756 	 * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
9757 	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
9758 	 */
9759 	return function(settings, schema) {
9760 		var self = this;
9761 
9762 		function noop() {}
9763 
9764 		settings = settings || {};
9765 		self.schema = schema = schema || new Schema();
9766 
9767 		if (settings.fix_self_closing !== false) {
9768 			settings.fix_self_closing = true;
9769 		}
9770 
9771 		// Add handler functions from settings and setup default handlers
9772 		each('comment cdata text start end pi doctype'.split(' '), function(name) {
9773 			if (name) {
9774 				self[name] = settings[name] || noop;
9775 			}
9776 		});
9777 
9778 		/**
9779 		 * Parses the specified HTML string and executes the callbacks for each item it finds.
9780 		 *
9781 		 * @example
9782 		 * new SaxParser({...}).parse('<b>text</b>');
9783 		 * @method parse
9784 		 * @param {String} html Html string to sax parse.
9785 		 */
9786 		self.parse = function(html) {
9787 			var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name;
9788 			var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded;
9789 			var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns;
9790 			var attributesRequired, attributesDefault, attributesForced;
9791 			var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0;
9792 			var decode = Entities.decode, fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href,data,background,formaction,poster');
9793 			var scriptUriRegExp = /((java|vb)script|mhtml):/i, dataUriRegExp = /^data:/i;
9794 
9795 			function processEndTag(name) {
9796 				var pos, i;
9797 
9798 				// Find position of parent of the same type
9799 				pos = stack.length;
9800 				while (pos--) {
9801 					if (stack[pos].name === name) {
9802 						break;
9803 					}
9804 				}
9805 
9806 				// Found parent
9807 				if (pos >= 0) {
9808 					// Close all the open elements
9809 					for (i = stack.length - 1; i >= pos; i--) {
9810 						name = stack[i];
9811 
9812 						if (name.valid) {
9813 							self.end(name.name);
9814 						}
9815 					}
9816 
9817 					// Remove the open elements from the stack
9818 					stack.length = pos;
9819 				}
9820 			}
9821 
9822 			function parseAttribute(match, name, value, val2, val3) {
9823 				var attrRule, i, trimRegExp = /[\s\u0000-\u001F]+/g;
9824 
9825 				name = name.toLowerCase();
9826 				value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
9827 
9828 				// Validate name and value pass through all data- attributes
9829 				if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
9830 					attrRule = validAttributesMap[name];
9831 
9832 					// Find rule by pattern matching
9833 					if (!attrRule && validAttributePatterns) {
9834 						i = validAttributePatterns.length;
9835 						while (i--) {
9836 							attrRule = validAttributePatterns[i];
9837 							if (attrRule.pattern.test(name)) {
9838 								break;
9839 							}
9840 						}
9841 
9842 						// No rule matched
9843 						if (i === -1) {
9844 							attrRule = null;
9845 						}
9846 					}
9847 
9848 					// No attribute rule found
9849 					if (!attrRule) {
9850 						return;
9851 					}
9852 
9853 					// Validate value
9854 					if (attrRule.validValues && !(value in attrRule.validValues)) {
9855 						return;
9856 					}
9857 				}
9858 
9859 				// Block any javascript: urls or non image data uris
9860 				if (filteredUrlAttrs[name] && !settings.allow_script_urls) {
9861 					var uri = value.replace(trimRegExp, '');
9862 
9863 					try {
9864 						// Might throw malformed URI sequence
9865 						uri = decodeURIComponent(uri);
9866 					} catch (ex) {
9867 						// Fallback to non UTF-8 decoder
9868 						uri = unescape(uri);
9869 					}
9870 
9871 					if (scriptUriRegExp.test(uri)) {
9872 						return;
9873 					}
9874 
9875 					if (!settings.allow_html_data_urls && dataUriRegExp.test(uri) && !/^data:image\//i.test(uri)) {
9876 						return;
9877 					}
9878 				}
9879 
9880 				// Add attribute to list and map
9881 				attrList.map[name] = value;
9882 				attrList.push({
9883 					name: name,
9884 					value: value
9885 				});
9886 			}
9887 
9888 			// Precompile RegExps and map objects
9889 			tokenRegExp = new RegExp('<(?:' +
9890 				'(?:!--([\\w\\W]*?)-->)|' + // Comment
9891 				'(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
9892 				'(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
9893 				'(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
9894 				'(?:\\/([^>]+)>)|' + // End element
9895 				'(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
9896 			')', 'g');
9897 
9898 			attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
9899 
9900 			// Setup lookup tables for empty elements and boolean attributes
9901 			shortEndedElements = schema.getShortEndedElements();
9902 			selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
9903 			fillAttrsMap = schema.getBoolAttrs();
9904 			validate = settings.validate;
9905 			removeInternalElements = settings.remove_internals;
9906 			fixSelfClosing = settings.fix_self_closing;
9907 			specialElements = schema.getSpecialElements();
9908 
9909 			while ((matches = tokenRegExp.exec(html))) {
9910 				// Text
9911 				if (index < matches.index) {
9912 					self.text(decode(html.substr(index, matches.index - index)));
9913 				}
9914 
9915 				if ((value = matches[6])) { // End element
9916 					value = value.toLowerCase();
9917 
9918 					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
9919 					if (value.charAt(0) === ':') {
9920 						value = value.substr(1);
9921 					}
9922 
9923 					processEndTag(value);
9924 				} else if ((value = matches[7])) { // Start element
9925 					value = value.toLowerCase();
9926 
9927 					// IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
9928 					if (value.charAt(0) === ':') {
9929 						value = value.substr(1);
9930 					}
9931 
9932 					isShortEnded = value in shortEndedElements;
9933 
9934 					// Is self closing tag for example an <li> after an open <li>
9935 					if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) {
9936 						processEndTag(value);
9937 					}
9938 
9939 					// Validate element
9940 					if (!validate || (elementRule = schema.getElementRule(value))) {
9941 						isValidElement = true;
9942 
9943 						// Grab attributes map and patters when validation is enabled
9944 						if (validate) {
9945 							validAttributesMap = elementRule.attributes;
9946 							validAttributePatterns = elementRule.attributePatterns;
9947 						}
9948 
9949 						// Parse attributes
9950 						if ((attribsValue = matches[8])) {
9951 							isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element
9952 
9953 							// If the element has internal attributes then remove it if we are told to do so
9954 							if (isInternalElement && removeInternalElements) {
9955 								isValidElement = false;
9956 							}
9957 
9958 							attrList = [];
9959 							attrList.map = {};
9960 
9961 							attribsValue.replace(attrRegExp, parseAttribute);
9962 						} else {
9963 							attrList = [];
9964 							attrList.map = {};
9965 						}
9966 
9967 						// Process attributes if validation is enabled
9968 						if (validate && !isInternalElement) {
9969 							attributesRequired = elementRule.attributesRequired;
9970 							attributesDefault = elementRule.attributesDefault;
9971 							attributesForced = elementRule.attributesForced;
9972 							anyAttributesRequired = elementRule.removeEmptyAttrs;
9973 
9974 							// Check if any attribute exists
9975 							if (anyAttributesRequired && !attrList.length) {
9976 								isValidElement = false;
9977 							}
9978 
9979 							// Handle forced attributes
9980 							if (attributesForced) {
9981 								i = attributesForced.length;
9982 								while (i--) {
9983 									attr = attributesForced[i];
9984 									name = attr.name;
9985 									attrValue = attr.value;
9986 
9987 									if (attrValue === '{$uid}') {
9988 										attrValue = 'mce_' + idCount++;
9989 									}
9990 
9991 									attrList.map[name] = attrValue;
9992 									attrList.push({name: name, value: attrValue});
9993 								}
9994 							}
9995 
9996 							// Handle default attributes
9997 							if (attributesDefault) {
9998 								i = attributesDefault.length;
9999 								while (i--) {
10000 									attr = attributesDefault[i];
10001 									name = attr.name;
10002 
10003 									if (!(name in attrList.map)) {
10004 										attrValue = attr.value;
10005 
10006 										if (attrValue === '{$uid}') {
10007 											attrValue = 'mce_' + idCount++;
10008 										}
10009 
10010 										attrList.map[name] = attrValue;
10011 										attrList.push({name: name, value: attrValue});
10012 									}
10013 								}
10014 							}
10015 
10016 							// Handle required attributes
10017 							if (attributesRequired) {
10018 								i = attributesRequired.length;
10019 								while (i--) {
10020 									if (attributesRequired[i] in attrList.map) {
10021 										break;
10022 									}
10023 								}
10024 
10025 								// None of the required attributes where found
10026 								if (i === -1) {
10027 									isValidElement = false;
10028 								}
10029 							}
10030 
10031 							// Invalidate element if it's marked as bogus
10032 							if (attrList.map['data-mce-bogus']) {
10033 								isValidElement = false;
10034 							}
10035 						}
10036 
10037 						if (isValidElement) {
10038 							self.start(value, attrList, isShortEnded);
10039 						}
10040 					} else {
10041 						isValidElement = false;
10042 					}
10043 
10044 					// Treat script, noscript and style a bit different since they may include code that looks like elements
10045 					if ((endRegExp = specialElements[value])) {
10046 						endRegExp.lastIndex = index = matches.index + matches[0].length;
10047 
10048 						if ((matches = endRegExp.exec(html))) {
10049 							if (isValidElement) {
10050 								text = html.substr(index, matches.index - index);
10051 							}
10052 
10053 							index = matches.index + matches[0].length;
10054 						} else {
10055 							text = html.substr(index);
10056 							index = html.length;
10057 						}
10058 
10059 						if (isValidElement) {
10060 							if (text.length > 0) {
10061 								self.text(text, true);
10062 							}
10063 
10064 							self.end(value);
10065 						}
10066 
10067 						tokenRegExp.lastIndex = index;
10068 						continue;
10069 					}
10070 
10071 					// Push value on to stack
10072 					if (!isShortEnded) {
10073 						if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) {
10074 							stack.push({name: value, valid: isValidElement});
10075 						} else if (isValidElement) {
10076 							self.end(value);
10077 						}
10078 					}
10079 				} else if ((value = matches[1])) { // Comment
10080 					// Padd comment value to avoid browsers from parsing invalid comments as HTML
10081 					if (value.charAt(0) === '>') {
10082 						value = ' ' + value;
10083 					}
10084 
10085 					if (!settings.allow_conditional_comments && value.substr(0, 3) === '[if') {
10086 						value = ' ' + value;
10087 					}
10088 
10089 					self.comment(value);
10090 				} else if ((value = matches[2])) { // CDATA
10091 					self.cdata(value);
10092 				} else if ((value = matches[3])) { // DOCTYPE
10093 					self.doctype(value);
10094 				} else if ((value = matches[4])) { // PI
10095 					self.pi(value, matches[5]);
10096 				}
10097 
10098 				index = matches.index + matches[0].length;
10099 			}
10100 
10101 			// Text
10102 			if (index < html.length) {
10103 				self.text(decode(html.substr(index)));
10104 			}
10105 
10106 			// Close any open elements
10107 			for (i = stack.length - 1; i >= 0; i--) {
10108 				value = stack[i];
10109 
10110 				if (value.valid) {
10111 					self.end(value.name);
10112 				}
10113 			}
10114 		};
10115 	};
10116 });
10117 
10118 // Included from: js/tinymce/classes/html/DomParser.js
10119 
10120 /**
10121  * DomParser.js
10122  *
10123  * Copyright, Moxiecode Systems AB
10124  * Released under LGPL License.
10125  *
10126  * License: http://www.tinymce.com/license
10127  * Contributing: http://www.tinymce.com/contributing
10128  */
10129 
10130 /**
10131  * This class parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make
10132  * sure that the node tree is valid according to the specified schema.
10133  * So for example: <p>a<p>b</p>c</p> will become <p>a</p><p>b</p><p>c</p>
10134  *
10135  * @example
10136  * var parser = new tinymce.html.DomParser({validate: true}, schema);
10137  * var rootNode = parser.parse('<h1>content</h1>');
10138  *
10139  * @class tinymce.html.DomParser
10140  * @version 3.4
10141  */
10142 define("tinymce/html/DomParser", [
10143 	"tinymce/html/Node",
10144 	"tinymce/html/Schema",
10145 	"tinymce/html/SaxParser",
10146 	"tinymce/util/Tools"
10147 ], function(Node, Schema, SaxParser, Tools) {
10148 	var makeMap = Tools.makeMap, each = Tools.each, explode = Tools.explode, extend = Tools.extend;
10149 
10150 	/**
10151 	 * Constructs a new DomParser instance.
10152 	 *
10153 	 * @constructor
10154 	 * @method DomParser
10155 	 * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
10156 	 * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
10157 	 */
10158 	return function(settings, schema) {
10159 		var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
10160 
10161 		settings = settings || {};
10162 		settings.validate = "validate" in settings ? settings.validate : true;
10163 		settings.root_name = settings.root_name || 'body';
10164 		self.schema = schema = schema || new Schema();
10165 
10166 		function fixInvalidChildren(nodes) {
10167 			var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i;
10168 			var nonEmptyElements, nonSplitableElements, textBlockElements, sibling, nextNode;
10169 
10170 			nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table');
10171 			nonEmptyElements = schema.getNonEmptyElements();
10172 			textBlockElements = schema.getTextBlockElements();
10173 
10174 			for (ni = 0; ni < nodes.length; ni++) {
10175 				node = nodes[ni];
10176 
10177 				// Already removed or fixed
10178 				if (!node.parent || node.fixed) {
10179 					continue;
10180 				}
10181 
10182 				// If the invalid element is a text block and the text block is within a parent LI element
10183 				// Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office
10184 				if (textBlockElements[node.name] && node.parent.name == 'li') {
10185 					// Move sibling text blocks after LI element
10186 					sibling = node.next;
10187 					while (sibling) {
10188 						if (textBlockElements[sibling.name]) {
10189 							sibling.name = 'li';
10190 							sibling.fixed = true;
10191 							node.parent.insert(sibling, node.parent);
10192 						} else {
10193 							break;
10194 						}
10195 
10196 						sibling = sibling.next;
10197 					}
10198 
10199 					// Unwrap current text block
10200 					node.unwrap(node);
10201 					continue;
10202 				}
10203 
10204 				// Get list of all parent nodes until we find a valid parent to stick the child into
10205 				parents = [node];
10206 				for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) &&
10207 					!nonSplitableElements[parent.name]; parent = parent.parent) {
10208 					parents.push(parent);
10209 				}
10210 
10211 				// Found a suitable parent
10212 				if (parent && parents.length > 1) {
10213 					// Reverse the array since it makes looping easier
10214 					parents.reverse();
10215 
10216 					// Clone the related parent and insert that after the moved node
10217 					newParent = currentNode = self.filterNode(parents[0].clone());
10218 
10219 					// Start cloning and moving children on the left side of the target node
10220 					for (i = 0; i < parents.length - 1; i++) {
10221 						if (schema.isValidChild(currentNode.name, parents[i].name)) {
10222 							tempNode = self.filterNode(parents[i].clone());
10223 							currentNode.append(tempNode);
10224 						} else {
10225 							tempNode = currentNode;
10226 						}
10227 
10228 						for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1]; ) {
10229 							nextNode = childNode.next;
10230 							tempNode.append(childNode);
10231 							childNode = nextNode;
10232 						}
10233 
10234 						currentNode = tempNode;
10235 					}
10236 
10237 					if (!newParent.isEmpty(nonEmptyElements)) {
10238 						parent.insert(newParent, parents[0], true);
10239 						parent.insert(node, newParent);
10240 					} else {
10241 						parent.insert(node, parents[0], true);
10242 					}
10243 
10244 					// Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>
10245 					parent = parents[0];
10246 					if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {
10247 						parent.empty().remove();
10248 					}
10249 				} else if (node.parent) {
10250 					// If it's an LI try to find a UL/OL for it or wrap it
10251 					if (node.name === 'li') {
10252 						sibling = node.prev;
10253 						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
10254 							sibling.append(node);
10255 							continue;
10256 						}
10257 
10258 						sibling = node.next;
10259 						if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
10260 							sibling.insert(node, sibling.firstChild, true);
10261 							continue;
10262 						}
10263 
10264 						node.wrap(self.filterNode(new Node('ul', 1)));
10265 						continue;
10266 					}
10267 
10268 					// Try wrapping the element in a DIV
10269 					if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {
10270 						node.wrap(self.filterNode(new Node('div', 1)));
10271 					} else {
10272 						// We failed wrapping it, then remove or unwrap it
10273 						if (node.name === 'style' || node.name === 'script') {
10274 							node.empty().remove();
10275 						} else {
10276 							node.unwrap();
10277 						}
10278 					}
10279 				}
10280 			}
10281 		}
10282 
10283 		/**
10284 		 * Runs the specified node though the element and attributes filters.
10285 		 *
10286 		 * @method filterNode
10287 		 * @param {tinymce.html.Node} Node the node to run filters on.
10288 		 * @return {tinymce.html.Node} The passed in node.
10289 		 */
10290 		self.filterNode = function(node) {
10291 			var i, name, list;
10292 
10293 			// Run element filters
10294 			if (name in nodeFilters) {
10295 				list = matchedNodes[name];
10296 
10297 				if (list) {
10298 					list.push(node);
10299 				} else {
10300 					matchedNodes[name] = [node];
10301 				}
10302 			}
10303 
10304 			// Run attribute filters
10305 			i = attributeFilters.length;
10306 			while (i--) {
10307 				name = attributeFilters[i].name;
10308 
10309 				if (name in node.attributes.map) {
10310 					list = matchedAttributes[name];
10311 
10312 					if (list) {
10313 						list.push(node);
10314 					} else {
10315 						matchedAttributes[name] = [node];
10316 					}
10317 				}
10318 			}
10319 
10320 			return node;
10321 		};
10322 
10323 		/**
10324 		 * Adds a node filter function to the parser, the parser will collect the specified nodes by name
10325 		 * and then execute the callback ones it has finished parsing the document.
10326 		 *
10327 		 * @example
10328 		 * parser.addNodeFilter('p,h1', function(nodes, name) {
10329 		 *		for (var i = 0; i < nodes.length; i++) {
10330 		 *			console.log(nodes[i].name);
10331 		 *		}
10332 		 * });
10333 		 * @method addNodeFilter
10334 		 * @method {String} name Comma separated list of nodes to collect.
10335 		 * @param {function} callback Callback function to execute once it has collected nodes.
10336 		 */
10337 		self.addNodeFilter = function(name, callback) {
10338 			each(explode(name), function(name) {
10339 				var list = nodeFilters[name];
10340 
10341 				if (!list) {
10342 					nodeFilters[name] = list = [];
10343 				}
10344 
10345 				list.push(callback);
10346 			});
10347 		};
10348 
10349 		/**
10350 		 * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes
10351 		 * and then execute the callback ones it has finished parsing the document.
10352 		 *
10353 		 * @example
10354 		 * parser.addAttributeFilter('src,href', function(nodes, name) {
10355 		 *		for (var i = 0; i < nodes.length; i++) {
10356 		 *			console.log(nodes[i].name);
10357 		 *		}
10358 		 * });
10359 		 * @method addAttributeFilter
10360 		 * @method {String} name Comma separated list of nodes to collect.
10361 		 * @param {function} callback Callback function to execute once it has collected nodes.
10362 		 */
10363 		self.addAttributeFilter = function(name, callback) {
10364 			each(explode(name), function(name) {
10365 				var i;
10366 
10367 				for (i = 0; i < attributeFilters.length; i++) {
10368 					if (attributeFilters[i].name === name) {
10369 						attributeFilters[i].callbacks.push(callback);
10370 						return;
10371 					}
10372 				}
10373 
10374 				attributeFilters.push({name: name, callbacks: [callback]});
10375 			});
10376 		};
10377 
10378 		/**
10379 		 * Parses the specified HTML string into a DOM like node tree and returns the result.
10380 		 *
10381 		 * @example
10382 		 * var rootNode = new DomParser({...}).parse('<b>text</b>');
10383 		 * @method parse
10384 		 * @param {String} html Html string to sax parse.
10385 		 * @param {Object} args Optional args object that gets passed to all filter functions.
10386 		 * @return {tinymce.html.Node} Root node containing the tree.
10387 		 */
10388 		self.parse = function(html, args) {
10389 			var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate;
10390 			var blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement;
10391 			var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements;
10392 			var children, nonEmptyElements, rootBlockName;
10393 
10394 			args = args || {};
10395 			matchedNodes = {};
10396 			matchedAttributes = {};
10397 			blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
10398 			nonEmptyElements = schema.getNonEmptyElements();
10399 			children = schema.children;
10400 			validate = settings.validate;
10401 			rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;
10402 
10403 			whiteSpaceElements = schema.getWhiteSpaceElements();
10404 			startWhiteSpaceRegExp = /^[ \t\r\n]+/;
10405 			endWhiteSpaceRegExp = /[ \t\r\n]+$/;
10406 			allWhiteSpaceRegExp = /[ \t\r\n]+/g;
10407 			isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
10408 
10409 			function addRootBlocks() {
10410 				var node = rootNode.firstChild, next, rootBlockNode;
10411 
10412 				// Removes whitespace at beginning and end of block so:
10413 				// <p> x </p> -> <p>x</p>
10414 				function trim(rootBlockNode) {
10415 					if (rootBlockNode) {
10416 						node = rootBlockNode.firstChild;
10417 						if (node && node.type == 3) {
10418 							node.value = node.value.replace(startWhiteSpaceRegExp, '');
10419 						}
10420 
10421 						node = rootBlockNode.lastChild;
10422 						if (node && node.type == 3) {
10423 							node.value = node.value.replace(endWhiteSpaceRegExp, '');
10424 						}
10425 					}
10426 				}
10427 
10428 				// Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root
10429 				if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
10430 					return;
10431 				}
10432 
10433 				while (node) {
10434 					next = node.next;
10435 
10436 					if (node.type == 3 || (node.type == 1 && node.name !== 'p' &&
10437 						!blockElements[node.name] && !node.attr('data-mce-type'))) {
10438 						if (!rootBlockNode) {
10439 							// Create a new root block element
10440 							rootBlockNode = createNode(rootBlockName, 1);
10441 							rootBlockNode.attr(settings.forced_root_block_attrs);
10442 							rootNode.insert(rootBlockNode, node);
10443 							rootBlockNode.append(node);
10444 						} else {
10445 							rootBlockNode.append(node);
10446 						}
10447 					} else {
10448 						trim(rootBlockNode);
10449 						rootBlockNode = null;
10450 					}
10451 
10452 					node = next;
10453 				}
10454 
10455 				trim(rootBlockNode);
10456 			}
10457 
10458 			function createNode(name, type) {
10459 				var node = new Node(name, type), list;
10460 
10461 				if (name in nodeFilters) {
10462 					list = matchedNodes[name];
10463 
10464 					if (list) {
10465 						list.push(node);
10466 					} else {
10467 						matchedNodes[name] = [node];
10468 					}
10469 				}
10470 
10471 				return node;
10472 			}
10473 
10474 			function removeWhitespaceBefore(node) {
10475 				var textNode, textVal, sibling;
10476 
10477 				for (textNode = node.prev; textNode && textNode.type === 3; ) {
10478 					textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
10479 
10480 					if (textVal.length > 0) {
10481 						textNode.value = textVal;
10482 						textNode = textNode.prev;
10483 					} else {
10484 						sibling = textNode.prev;
10485 						textNode.remove();
10486 						textNode = sibling;
10487 					}
10488 				}
10489 			}
10490 
10491 			function cloneAndExcludeBlocks(input) {
10492 				var name, output = {};
10493 
10494 				for (name in input) {
10495 					if (name !== 'li' && name != 'p') {
10496 						output[name] = input[name];
10497 					}
10498 				}
10499 
10500 				return output;
10501 			}
10502 
10503 			parser = new SaxParser({
10504 				validate: validate,
10505 				allow_script_urls: settings.allow_script_urls,
10506 				allow_conditional_comments: settings.allow_conditional_comments,
10507 
10508 				// Exclude P and LI from DOM parsing since it's treated better by the DOM parser
10509 				self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
10510 
10511 				cdata: function(text) {
10512 					node.append(createNode('#cdata', 4)).value = text;
10513 				},
10514 
10515 				text: function(text, raw) {
10516 					var textNode;
10517 
10518 					// Trim all redundant whitespace on non white space elements
10519 					if (!isInWhiteSpacePreservedElement) {
10520 						text = text.replace(allWhiteSpaceRegExp, ' ');
10521 
10522 						if (node.lastChild && blockElements[node.lastChild.name]) {
10523 							text = text.replace(startWhiteSpaceRegExp, '');
10524 						}
10525 					}
10526 
10527 					// Do we need to create the node
10528 					if (text.length !== 0) {
10529 						textNode = createNode('#text', 3);
10530 						textNode.raw = !!raw;
10531 						node.append(textNode).value = text;
10532 					}
10533 				},
10534 
10535 				comment: function(text) {
10536 					node.append(createNode('#comment', 8)).value = text;
10537 				},
10538 
10539 				pi: function(name, text) {
10540 					node.append(createNode(name, 7)).value = text;
10541 					removeWhitespaceBefore(node);
10542 				},
10543 
10544 				doctype: function(text) {
10545 					var newNode;
10546 
10547 					newNode = node.append(createNode('#doctype', 10));
10548 					newNode.value = text;
10549 					removeWhitespaceBefore(node);
10550 				},
10551 
10552 				start: function(name, attrs, empty) {
10553 					var newNode, attrFiltersLen, elementRule, attrName, parent;
10554 
10555 					elementRule = validate ? schema.getElementRule(name) : {};
10556 					if (elementRule) {
10557 						newNode = createNode(elementRule.outputName || name, 1);
10558 						newNode.attributes = attrs;
10559 						newNode.shortEnded = empty;
10560 
10561 						node.append(newNode);
10562 
10563 						// Check if node is valid child of the parent node is the child is
10564 						// unknown we don't collect it since it's probably a custom element
10565 						parent = children[node.name];
10566 						if (parent && children[newNode.name] && !parent[newNode.name]) {
10567 							invalidChildren.push(newNode);
10568 						}
10569 
10570 						attrFiltersLen = attributeFilters.length;
10571 						while (attrFiltersLen--) {
10572 							attrName = attributeFilters[attrFiltersLen].name;
10573 
10574 							if (attrName in attrs.map) {
10575 								list = matchedAttributes[attrName];
10576 
10577 								if (list) {
10578 									list.push(newNode);
10579 								} else {
10580 									matchedAttributes[attrName] = [newNode];
10581 								}
10582 							}
10583 						}
10584 
10585 						// Trim whitespace before block
10586 						if (blockElements[name]) {
10587 							removeWhitespaceBefore(newNode);
10588 						}
10589 
10590 						// Change current node if the element wasn't empty i.e not <br /> or <img />
10591 						if (!empty) {
10592 							node = newNode;
10593 						}
10594 
10595 						// Check if we are inside a whitespace preserved element
10596 						if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
10597 							isInWhiteSpacePreservedElement = true;
10598 						}
10599 					}
10600 				},
10601 
10602 				end: function(name) {
10603 					var textNode, elementRule, text, sibling, tempNode;
10604 
10605 					elementRule = validate ? schema.getElementRule(name) : {};
10606 					if (elementRule) {
10607 						if (blockElements[name]) {
10608 							if (!isInWhiteSpacePreservedElement) {
10609 								// Trim whitespace of the first node in a block
10610 								textNode = node.firstChild;
10611 								if (textNode && textNode.type === 3) {
10612 									text = textNode.value.replace(startWhiteSpaceRegExp, '');
10613 
10614 									// Any characters left after trim or should we remove it
10615 									if (text.length > 0) {
10616 										textNode.value = text;
10617 										textNode = textNode.next;
10618 									} else {
10619 										sibling = textNode.next;
10620 										textNode.remove();
10621 										textNode = sibling;
10622 
10623 										// Remove any pure whitespace siblings
10624 										while (textNode && textNode.type === 3) {
10625 											text = textNode.value;
10626 											sibling = textNode.next;
10627 
10628 											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
10629 												textNode.remove();
10630 												textNode = sibling;
10631 											}
10632 
10633 											textNode = sibling;
10634 										}
10635 									}
10636 								}
10637 
10638 								// Trim whitespace of the last node in a block
10639 								textNode = node.lastChild;
10640 								if (textNode && textNode.type === 3) {
10641 									text = textNode.value.replace(endWhiteSpaceRegExp, '');
10642 
10643 									// Any characters left after trim or should we remove it
10644 									if (text.length > 0) {
10645 										textNode.value = text;
10646 										textNode = textNode.prev;
10647 									} else {
10648 										sibling = textNode.prev;
10649 										textNode.remove();
10650 										textNode = sibling;
10651 
10652 										// Remove any pure whitespace siblings
10653 										while (textNode && textNode.type === 3) {
10654 											text = textNode.value;
10655 											sibling = textNode.prev;
10656 
10657 											if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
10658 												textNode.remove();
10659 												textNode = sibling;
10660 											}
10661 
10662 											textNode = sibling;
10663 										}
10664 									}
10665 								}
10666 							}
10667 
10668 							// Trim start white space
10669 							// Removed due to: #5424
10670 							/*textNode = node.prev;
10671 							if (textNode && textNode.type === 3) {
10672 								text = textNode.value.replace(startWhiteSpaceRegExp, '');
10673 
10674 								if (text.length > 0)
10675 									textNode.value = text;
10676 								else
10677 									textNode.remove();
10678 							}*/
10679 						}
10680 
10681 						// Check if we exited a whitespace preserved element
10682 						if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
10683 							isInWhiteSpacePreservedElement = false;
10684 						}
10685 
10686 						// Handle empty nodes
10687 						if (elementRule.removeEmpty || elementRule.paddEmpty) {
10688 							if (node.isEmpty(nonEmptyElements)) {
10689 								if (elementRule.paddEmpty) {
10690 									node.empty().append(new Node('#text', '3')).value = '\u00a0';
10691 								} else {
10692 									// Leave nodes that have a name like <a name="name">
10693 									if (!node.attributes.map.name && !node.attributes.map.id) {
10694 										tempNode = node.parent;
10695 										node.empty().remove();
10696 										node = tempNode;
10697 										return;
10698 									}
10699 								}
10700 							}
10701 						}
10702 
10703 						node = node.parent;
10704 					}
10705 				}
10706 			}, schema);
10707 
10708 			rootNode = node = new Node(args.context || settings.root_name, 11);
10709 
10710 			parser.parse(html);
10711 
10712 			// Fix invalid children or report invalid children in a contextual parsing
10713 			if (validate && invalidChildren.length) {
10714 				if (!args.context) {
10715 					fixInvalidChildren(invalidChildren);
10716 				} else {
10717 					args.invalid = true;
10718 				}
10719 			}
10720 
10721 			// Wrap nodes in the root into block elements if the root is body
10722 			if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) {
10723 				addRootBlocks();
10724 			}
10725 
10726 			// Run filters only when the contents is valid
10727 			if (!args.invalid) {
10728 				// Run node filters
10729 				for (name in matchedNodes) {
10730 					list = nodeFilters[name];
10731 					nodes = matchedNodes[name];
10732 
10733 					// Remove already removed children
10734 					fi = nodes.length;
10735 					while (fi--) {
10736 						if (!nodes[fi].parent) {
10737 							nodes.splice(fi, 1);
10738 						}
10739 					}
10740 
10741 					for (i = 0, l = list.length; i < l; i++) {
10742 						list[i](nodes, name, args);
10743 					}
10744 				}
10745 
10746 				// Run attribute filters
10747 				for (i = 0, l = attributeFilters.length; i < l; i++) {
10748 					list = attributeFilters[i];
10749 
10750 					if (list.name in matchedAttributes) {
10751 						nodes = matchedAttributes[list.name];
10752 
10753 						// Remove already removed children
10754 						fi = nodes.length;
10755 						while (fi--) {
10756 							if (!nodes[fi].parent) {
10757 								nodes.splice(fi, 1);
10758 							}
10759 						}
10760 
10761 						for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) {
10762 							list.callbacks[fi](nodes, list.name, args);
10763 						}
10764 					}
10765 				}
10766 			}
10767 
10768 			return rootNode;
10769 		};
10770 
10771 		// Remove <br> at end of block elements Gecko and WebKit injects BR elements to
10772 		// make it possible to place the caret inside empty blocks. This logic tries to remove
10773 		// these elements and keep br elements that where intended to be there intact
10774 		if (settings.remove_trailing_brs) {
10775 			self.addNodeFilter('br', function(nodes) {
10776 				var i, l = nodes.length, node, blockElements = extend({}, schema.getBlockElements());
10777 				var nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
10778 				var elementRule, textNode;
10779 
10780 				// Remove brs from body element as well
10781 				blockElements.body = 1;
10782 
10783 				// Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>
10784 				for (i = 0; i < l; i++) {
10785 					node = nodes[i];
10786 					parent = node.parent;
10787 
10788 					if (blockElements[node.parent.name] && node === parent.lastChild) {
10789 						// Loop all nodes to the left of the current node and check for other BR elements
10790 						// excluding bookmarks since they are invisible
10791 						prev = node.prev;
10792 						while (prev) {
10793 							prevName = prev.name;
10794 
10795 							// Ignore bookmarks
10796 							if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {
10797 								// Found a non BR element
10798 								if (prevName !== "br") {
10799 									break;
10800 								}
10801 
10802 								// Found another br it's a <br><br> structure then don't remove anything
10803 								if (prevName === 'br') {
10804 									node = null;
10805 									break;
10806 								}
10807 							}
10808 
10809 							prev = prev.prev;
10810 						}
10811 
10812 						if (node) {
10813 							node.remove();
10814 
10815 							// Is the parent to be considered empty after we removed the BR
10816 							if (parent.isEmpty(nonEmptyElements)) {
10817 								elementRule = schema.getElementRule(parent.name);
10818 
10819 								// Remove or padd the element depending on schema rule
10820 								if (elementRule) {
10821 									if (elementRule.removeEmpty) {
10822 										parent.remove();
10823 									} else if (elementRule.paddEmpty) {
10824 										parent.empty().append(new Node('#text', 3)).value = '\u00a0';
10825 									}
10826 								}
10827 							}
10828 						}
10829 					} else {
10830 						// Replaces BR elements inside inline elements like <p><b><i><br></i></b></p>
10831 						// so they become <p><b><i> </i></b></p>
10832 						lastParent = node;
10833 						while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) {
10834 							lastParent = parent;
10835 
10836 							if (blockElements[parent.name]) {
10837 								break;
10838 							}
10839 
10840 							parent = parent.parent;
10841 						}
10842 
10843 						if (lastParent === parent) {
10844 							textNode = new Node('#text', 3);
10845 							textNode.value = '\u00a0';
10846 							node.replace(textNode);
10847 						}
10848 					}
10849 				}
10850 			});
10851 		}
10852 
10853 		// Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
10854 		if (!settings.allow_html_in_named_anchor) {
10855 			self.addAttributeFilter('id,name', function(nodes) {
10856 				var i = nodes.length, sibling, prevSibling, parent, node;
10857 
10858 				while (i--) {
10859 					node = nodes[i];
10860 					if (node.name === 'a' && node.firstChild && !node.attr('href')) {
10861 						parent = node.parent;
10862 
10863 						// Move children after current node
10864 						sibling = node.lastChild;
10865 						do {
10866 							prevSibling = sibling.prev;
10867 							parent.insert(sibling, node);
10868 							sibling = prevSibling;
10869 						} while (sibling);
10870 					}
10871 				}
10872 			});
10873 		}
10874 	};
10875 });
10876 
10877 // Included from: js/tinymce/classes/html/Writer.js
10878 
10879 /**
10880  * Writer.js
10881  *
10882  * Copyright, Moxiecode Systems AB
10883  * Released under LGPL License.
10884  *
10885  * License: http://www.tinymce.com/license
10886  * Contributing: http://www.tinymce.com/contributing
10887  */
10888 
10889 /**
10890  * This class is used to write HTML tags out it can be used with the Serializer or the SaxParser.
10891  *
10892  * @class tinymce.html.Writer
10893  * @example
10894  * var writer = new tinymce.html.Writer({indent: true});
10895  * var parser = new tinymce.html.SaxParser(writer).parse('<p><br></p>');
10896  * console.log(writer.getContent());
10897  *
10898  * @class tinymce.html.Writer
10899  * @version 3.4
10900  */
10901 define("tinymce/html/Writer", [
10902 	"tinymce/html/Entities",
10903 	"tinymce/util/Tools"
10904 ], function(Entities, Tools) {
10905 	var makeMap = Tools.makeMap;
10906 
10907 	/**
10908 	 * Constructs a new Writer instance.
10909 	 *
10910 	 * @constructor
10911 	 * @method Writer
10912 	 * @param {Object} settings Name/value settings object.
10913 	 */
10914 	return function(settings) {
10915 		var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;
10916 
10917 		settings = settings || {};
10918 		indent = settings.indent;
10919 		indentBefore = makeMap(settings.indent_before || '');
10920 		indentAfter = makeMap(settings.indent_after || '');
10921 		encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
10922 		htmlOutput = settings.element_format == "html";
10923 
10924 		return {
10925 			/**
10926 			 * Writes the a start element such as <p id="a">.
10927 			 *
10928 			 * @method start
10929 			 * @param {String} name Name of the element.
10930 			 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
10931 			 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
10932 			 */
10933 			start: function(name, attrs, empty) {
10934 				var i, l, attr, value;
10935 
10936 				if (indent && indentBefore[name] && html.length > 0) {
10937 					value = html[html.length - 1];
10938 
10939 					if (value.length > 0 && value !== '\n') {
10940 						html.push('\n');
10941 					}
10942 				}
10943 
10944 				html.push('<', name);
10945 
10946 				if (attrs) {
10947 					for (i = 0, l = attrs.length; i < l; i++) {
10948 						attr = attrs[i];
10949 						html.push(' ', attr.name, '="', encode(attr.value, true), '"');
10950 					}
10951 				}
10952 
10953 				if (!empty || htmlOutput) {
10954 					html[html.length] = '>';
10955 				} else {
10956 					html[html.length] = ' />';
10957 				}
10958 
10959 				if (empty && indent && indentAfter[name] && html.length > 0) {
10960 					value = html[html.length - 1];
10961 
10962 					if (value.length > 0 && value !== '\n') {
10963 						html.push('\n');
10964 					}
10965 				}
10966 			},
10967 
10968 			/**
10969 			 * Writes the a end element such as </p>.
10970 			 *
10971 			 * @method end
10972 			 * @param {String} name Name of the element.
10973 			 */
10974 			end: function(name) {
10975 				var value;
10976 
10977 				/*if (indent && indentBefore[name] && html.length > 0) {
10978 					value = html[html.length - 1];
10979 
10980 					if (value.length > 0 && value !== '\n')
10981 						html.push('\n');
10982 				}*/
10983 
10984 				html.push('</', name, '>');
10985 
10986 				if (indent && indentAfter[name] && html.length > 0) {
10987 					value = html[html.length - 1];
10988 
10989 					if (value.length > 0 && value !== '\n') {
10990 						html.push('\n');
10991 					}
10992 				}
10993 			},
10994 
10995 			/**
10996 			 * Writes a text node.
10997 			 *
10998 			 * @method text
10999 			 * @param {String} text String to write out.
11000 			 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
11001 			 */
11002 			text: function(text, raw) {
11003 				if (text.length > 0) {
11004 					html[html.length] = raw ? text : encode(text);
11005 				}
11006 			},
11007 
11008 			/**
11009 			 * Writes a cdata node such as <![CDATA[data]]>.
11010 			 *
11011 			 * @method cdata
11012 			 * @param {String} text String to write out inside the cdata.
11013 			 */
11014 			cdata: function(text) {
11015 				html.push('<![CDATA[', text, ']]>');
11016 			},
11017 
11018 			/**
11019 			 * Writes a comment node such as <!-- Comment -->.
11020 			 *
11021 			 * @method cdata
11022 			 * @param {String} text String to write out inside the comment.
11023 			 */
11024 			comment: function(text) {
11025 				html.push('<!--', text, '-->');
11026 			},
11027 
11028 			/**
11029 			 * Writes a PI node such as <?xml attr="value" ?>.
11030 			 *
11031 			 * @method pi
11032 			 * @param {String} name Name of the pi.
11033 			 * @param {String} text String to write out inside the pi.
11034 			 */
11035 			pi: function(name, text) {
11036 				if (text) {
11037 					html.push('<?', name, ' ', text, '?>');
11038 				} else {
11039 					html.push('<?', name, '?>');
11040 				}
11041 
11042 				if (indent) {
11043 					html.push('\n');
11044 				}
11045 			},
11046 
11047 			/**
11048 			 * Writes a doctype node such as <!DOCTYPE data>.
11049 			 *
11050 			 * @method doctype
11051 			 * @param {String} text String to write out inside the doctype.
11052 			 */
11053 			doctype: function(text) {
11054 				html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');
11055 			},
11056 
11057 			/**
11058 			 * Resets the internal buffer if one wants to reuse the writer.
11059 			 *
11060 			 * @method reset
11061 			 */
11062 			reset: function() {
11063 				html.length = 0;
11064 			},
11065 
11066 			/**
11067 			 * Returns the contents that got serialized.
11068 			 *
11069 			 * @method getContent
11070 			 * @return {String} HTML contents that got written down.
11071 			 */
11072 			getContent: function() {
11073 				return html.join('').replace(/\n$/, '');
11074 			}
11075 		};
11076 	};
11077 });
11078 
11079 // Included from: js/tinymce/classes/html/Serializer.js
11080 
11081 /**
11082  * Serializer.js
11083  *
11084  * Copyright, Moxiecode Systems AB
11085  * Released under LGPL License.
11086  *
11087  * License: http://www.tinymce.com/license
11088  * Contributing: http://www.tinymce.com/contributing
11089  */
11090 
11091 /**
11092  * This class is used to serialize down the DOM tree into a string using a Writer instance.
11093  *
11094  *
11095  * @example
11096  * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
11097  * @class tinymce.html.Serializer
11098  * @version 3.4
11099  */
11100 define("tinymce/html/Serializer", [
11101 	"tinymce/html/Writer",
11102 	"tinymce/html/Schema"
11103 ], function(Writer, Schema) {
11104 	/**
11105 	 * Constructs a new Serializer instance.
11106 	 *
11107 	 * @constructor
11108 	 * @method Serializer
11109 	 * @param {Object} settings Name/value settings object.
11110 	 * @param {tinymce.html.Schema} schema Schema instance to use.
11111 	 */
11112 	return function(settings, schema) {
11113 		var self = this, writer = new Writer(settings);
11114 
11115 		settings = settings || {};
11116 		settings.validate = "validate" in settings ? settings.validate : true;
11117 
11118 		self.schema = schema = schema || new Schema();
11119 		self.writer = writer;
11120 
11121 		/**
11122 		 * Serializes the specified node into a string.
11123 		 *
11124 		 * @example
11125 		 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
11126 		 * @method serialize
11127 		 * @param {tinymce.html.Node} node Node instance to serialize.
11128 		 * @return {String} String with HTML based on DOM tree.
11129 		 */
11130 		self.serialize = function(node) {
11131 			var handlers, validate;
11132 
11133 			validate = settings.validate;
11134 
11135 			handlers = {
11136 				// #text
11137 				3: function(node) {
11138 					writer.text(node.value, node.raw);
11139 				},
11140 
11141 				// #comment
11142 				8: function(node) {
11143 					writer.comment(node.value);
11144 				},
11145 
11146 				// Processing instruction
11147 				7: function(node) {
11148 					writer.pi(node.name, node.value);
11149 				},
11150 
11151 				// Doctype
11152 				10: function(node) {
11153 					writer.doctype(node.value);
11154 				},
11155 
11156 				// CDATA
11157 				4: function(node) {
11158 					writer.cdata(node.value);
11159 				},
11160 
11161 				// Document fragment
11162 				11: function(node) {
11163 					if ((node = node.firstChild)) {
11164 						do {
11165 							walk(node);
11166 						} while ((node = node.next));
11167 					}
11168 				}
11169 			};
11170 
11171 			writer.reset();
11172 
11173 			function walk(node) {
11174 				var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;
11175 
11176 				if (!handler) {
11177 					name = node.name;
11178 					isEmpty = node.shortEnded;
11179 					attrs = node.attributes;
11180 
11181 					// Sort attributes
11182 					if (validate && attrs && attrs.length > 1) {
11183 						sortedAttrs = [];
11184 						sortedAttrs.map = {};
11185 
11186 						elementRule = schema.getElementRule(node.name);
11187 						for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
11188 							attrName = elementRule.attributesOrder[i];
11189 
11190 							if (attrName in attrs.map) {
11191 								attrValue = attrs.map[attrName];
11192 								sortedAttrs.map[attrName] = attrValue;
11193 								sortedAttrs.push({name: attrName, value: attrValue});
11194 							}
11195 						}
11196 
11197 						for (i = 0, l = attrs.length; i < l; i++) {
11198 							attrName = attrs[i].name;
11199 
11200 							if (!(attrName in sortedAttrs.map)) {
11201 								attrValue = attrs.map[attrName];
11202 								sortedAttrs.map[attrName] = attrValue;
11203 								sortedAttrs.push({name: attrName, value: attrValue});
11204 							}
11205 						}
11206 
11207 						attrs = sortedAttrs;
11208 					}
11209 
11210 					writer.start(node.name, attrs, isEmpty);
11211 
11212 					if (!isEmpty) {
11213 						if ((node = node.firstChild)) {
11214 							do {
11215 								walk(node);
11216 							} while ((node = node.next));
11217 						}
11218 
11219 						writer.end(name);
11220 					}
11221 				} else {
11222 					handler(node);
11223 				}
11224 			}
11225 
11226 			// Serialize element and treat all non elements as fragments
11227 			if (node.type == 1 && !settings.inner) {
11228 				walk(node);
11229 			} else {
11230 				handlers[11](node);
11231 			}
11232 
11233 			return writer.getContent();
11234 		};
11235 	};
11236 });
11237 
11238 // Included from: js/tinymce/classes/dom/Serializer.js
11239 
11240 /**
11241  * Serializer.js
11242  *
11243  * Copyright, Moxiecode Systems AB
11244  * Released under LGPL License.
11245  *
11246  * License: http://www.tinymce.com/license
11247  * Contributing: http://www.tinymce.com/contributing
11248  */
11249 
11250 /**
11251  * This class is used to serialize DOM trees into a string. Consult the TinyMCE Wiki API for
11252  * more details and examples on how to use this class.
11253  *
11254  * @class tinymce.dom.Serializer
11255  */
11256 define("tinymce/dom/Serializer", [
11257 	"tinymce/dom/DOMUtils",
11258 	"tinymce/html/DomParser",
11259 	"tinymce/html/Entities",
11260 	"tinymce/html/Serializer",
11261 	"tinymce/html/Node",
11262 	"tinymce/html/Schema",
11263 	"tinymce/Env",
11264 	"tinymce/util/Tools"
11265 ], function(DOMUtils, DomParser, Entities, Serializer, Node, Schema, Env, Tools) {
11266 	var each = Tools.each, trim = Tools.trim;
11267 	var DOM = DOMUtils.DOM;
11268 
11269 	/**
11270 	 * Constructs a new DOM serializer class.
11271 	 *
11272 	 * @constructor
11273 	 * @method Serializer
11274 	 * @param {Object} settings Serializer settings object.
11275 	 * @param {tinymce.Editor} editor Optional editor to bind events to and get schema/dom from.
11276 	 */
11277 	return function(settings, editor) {
11278 		var dom, schema, htmlParser;
11279 
11280 		if (editor) {
11281 			dom = editor.dom;
11282 			schema = editor.schema;
11283 		}
11284 
11285 		// Default DOM and Schema if they are undefined
11286 		dom = dom || DOM;
11287 		schema = schema || new Schema(settings);
11288 		settings.entity_encoding = settings.entity_encoding || 'named';
11289 		settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
11290 
11291 		htmlParser = new DomParser(settings, schema);
11292 
11293 		// Convert tabindex back to elements when serializing contents
11294 		htmlParser.addAttributeFilter('data-mce-tabindex', function(nodes, name) {
11295 			var i = nodes.length, node;
11296 
11297 			while (i--) {
11298 				node = nodes[i];
11299 				node.attr('tabindex', node.attributes.map['data-mce-tabindex']);
11300 				node.attr(name, null);
11301 			}
11302 		});
11303 
11304 		// Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed
11305 		htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {
11306 			var i = nodes.length, node, value, internalName = 'data-mce-' + name;
11307 			var urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;
11308 
11309 			while (i--) {
11310 				node = nodes[i];
11311 
11312 				value = node.attributes.map[internalName];
11313 				if (value !== undef) {
11314 					// Set external name to internal value and remove internal
11315 					node.attr(name, value.length > 0 ? value : null);
11316 					node.attr(internalName, null);
11317 				} else {
11318 					// No internal attribute found then convert the value we have in the DOM
11319 					value = node.attributes.map[name];
11320 
11321 					if (name === "style") {
11322 						value = dom.serializeStyle(dom.parseStyle(value), node.name);
11323 					} else if (urlConverter) {
11324 						value = urlConverter.call(urlConverterScope, value, name, node.name);
11325 					}
11326 
11327 					node.attr(name, value.length > 0 ? value : null);
11328 				}
11329 			}
11330 		});
11331 
11332 		// Remove internal classes mceItem<..> or mceSelected
11333 		htmlParser.addAttributeFilter('class', function(nodes) {
11334 			var i = nodes.length, node, value;
11335 
11336 			while (i--) {
11337 				node = nodes[i];
11338 				value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, '');
11339 				node.attr('class', value.length > 0 ? value : null);
11340 			}
11341 		});
11342 
11343 		// Remove bookmark elements
11344 		htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {
11345 			var i = nodes.length, node;
11346 
11347 			while (i--) {
11348 				node = nodes[i];
11349 
11350 				if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup) {
11351 					node.remove();
11352 				}
11353 			}
11354 		});
11355 
11356 		// Remove expando attributes
11357 		htmlParser.addAttributeFilter('data-mce-expando', function(nodes, name) {
11358 			var i = nodes.length;
11359 
11360 			while (i--) {
11361 				nodes[i].attr(name, null);
11362 			}
11363 		});
11364 
11365 		htmlParser.addNodeFilter('noscript', function(nodes) {
11366 			var i = nodes.length, node;
11367 
11368 			while (i--) {
11369 				node = nodes[i].firstChild;
11370 
11371 				if (node) {
11372 					node.value = Entities.decode(node.value);
11373 				}
11374 			}
11375 		});
11376 
11377 		// Force script into CDATA sections and remove the mce- prefix also add comments around styles
11378 		htmlParser.addNodeFilter('script,style', function(nodes, name) {
11379 			var i = nodes.length, node, value;
11380 
11381 			function trim(value) {
11382 				/*jshint maxlen:255 */
11383 				/*eslint max-len:0 */
11384 				return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
11385 						.replace(/^[\r\n]*|[\r\n]*$/g, '')
11386 						.replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
11387 						.replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
11388 			}
11389 
11390 			while (i--) {
11391 				node = nodes[i];
11392 				value = node.firstChild ? node.firstChild.value : '';
11393 
11394 				if (name === "script") {
11395 					// Remove mce- prefix from script elements and remove default text/javascript mime type (HTML5)
11396 					var type = (node.attr('type') || 'text/javascript').replace(/^mce\-/, '');
11397 					node.attr('type', type === 'text/javascript' ? null : type);
11398 
11399 					if (value.length > 0) {
11400 						node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
11401 					}
11402 				} else {
11403 					if (value.length > 0) {
11404 						node.firstChild.value = '<!--\n' + trim(value) + '\n-->';
11405 					}
11406 				}
11407 			}
11408 		});
11409 
11410 		// Convert comments to cdata and handle protected comments
11411 		htmlParser.addNodeFilter('#comment', function(nodes) {
11412 			var i = nodes.length, node;
11413 
11414 			while (i--) {
11415 				node = nodes[i];
11416 
11417 				if (node.value.indexOf('[CDATA[') === 0) {
11418 					node.name = '#cdata';
11419 					node.type = 4;
11420 					node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');
11421 				} else if (node.value.indexOf('mce:protected ') === 0) {
11422 					node.name = "#text";
11423 					node.type = 3;
11424 					node.raw = true;
11425 					node.value = unescape(node.value).substr(14);
11426 				}
11427 			}
11428 		});
11429 
11430 		htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {
11431 			var i = nodes.length, node;
11432 
11433 			while (i--) {
11434 				node = nodes[i];
11435 				if (node.type === 7) {
11436 					node.remove();
11437 				} else if (node.type === 1) {
11438 					if (name === "input" && !("type" in node.attributes.map)) {
11439 						node.attr('type', 'text');
11440 					}
11441 				}
11442 			}
11443 		});
11444 
11445 		// Fix list elements, TODO: Replace this later
11446 		if (settings.fix_list_elements) {
11447 			htmlParser.addNodeFilter('ul,ol', function(nodes) {
11448 				var i = nodes.length, node, parentNode;
11449 
11450 				while (i--) {
11451 					node = nodes[i];
11452 					parentNode = node.parent;
11453 
11454 					if (parentNode.name === 'ul' || parentNode.name === 'ol') {
11455 						if (node.prev && node.prev.name === 'li') {
11456 							node.prev.append(node);
11457 						}
11458 					}
11459 				}
11460 			});
11461 		}
11462 
11463 		// Remove internal data attributes
11464 		htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style,data-mce-selected', function(nodes, name) {
11465 			var i = nodes.length;
11466 
11467 			while (i--) {
11468 				nodes[i].attr(name, null);
11469 			}
11470 		});
11471 
11472 		// Return public methods
11473 		return {
11474 			/**
11475 			 * Schema instance that was used to when the Serializer was constructed.
11476 			 *
11477 			 * @field {tinymce.html.Schema} schema
11478 			 */
11479 			schema: schema,
11480 
11481 			/**
11482 			 * Adds a node filter function to the parser used by the serializer, the parser will collect the specified nodes by name
11483 			 * and then execute the callback ones it has finished parsing the document.
11484 			 *
11485 			 * @example
11486 			 * parser.addNodeFilter('p,h1', function(nodes, name) {
11487 			 *		for (var i = 0; i < nodes.length; i++) {
11488 			 *			console.log(nodes[i].name);
11489 			 *		}
11490 			 * });
11491 			 * @method addNodeFilter
11492 			 * @method {String} name Comma separated list of nodes to collect.
11493 			 * @param {function} callback Callback function to execute once it has collected nodes.
11494 			 */
11495 			addNodeFilter: htmlParser.addNodeFilter,
11496 
11497 			/**
11498 			 * Adds a attribute filter function to the parser used by the serializer, the parser will
11499 			 * collect nodes that has the specified attributes
11500 			 * and then execute the callback ones it has finished parsing the document.
11501 			 *
11502 			 * @example
11503 			 * parser.addAttributeFilter('src,href', function(nodes, name) {
11504 			 *		for (var i = 0; i < nodes.length; i++) {
11505 			 *			console.log(nodes[i].name);
11506 			 *		}
11507 			 * });
11508 			 * @method addAttributeFilter
11509 			 * @method {String} name Comma separated list of nodes to collect.
11510 			 * @param {function} callback Callback function to execute once it has collected nodes.
11511 			 */
11512 			addAttributeFilter: htmlParser.addAttributeFilter,
11513 
11514 			/**
11515 			 * Serializes the specified browser DOM node into a HTML string.
11516 			 *
11517 			 * @method serialize
11518 			 * @param {DOMNode} node DOM node to serialize.
11519 			 * @param {Object} args Arguments option that gets passed to event handlers.
11520 			 */
11521 			serialize: function(node, args) {
11522 				var self = this, impl, doc, oldDoc, htmlSerializer, content;
11523 
11524 				// Explorer won't clone contents of script and style and the
11525 				// selected index of select elements are cleared on a clone operation.
11526 				if (Env.ie && dom.select('script,style,select,map').length > 0) {
11527 					content = node.innerHTML;
11528 					node = node.cloneNode(false);
11529 					dom.setHTML(node, content);
11530 				} else {
11531 					node = node.cloneNode(true);
11532 				}
11533 
11534 				// Nodes needs to be attached to something in WebKit/Opera
11535 				// This fix will make DOM ranges and make Sizzle happy!
11536 				impl = node.ownerDocument.implementation;
11537 				if (impl.createHTMLDocument) {
11538 					// Create an empty HTML document
11539 					doc = impl.createHTMLDocument("");
11540 
11541 					// Add the element or it's children if it's a body element to the new document
11542 					each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {
11543 						doc.body.appendChild(doc.importNode(node, true));
11544 					});
11545 
11546 					// Grab first child or body element for serialization
11547 					if (node.nodeName != 'BODY') {
11548 						node = doc.body.firstChild;
11549 					} else {
11550 						node = doc.body;
11551 					}
11552 
11553 					// set the new document in DOMUtils so createElement etc works
11554 					oldDoc = dom.doc;
11555 					dom.doc = doc;
11556 				}
11557 
11558 				args = args || {};
11559 				args.format = args.format || 'html';
11560 
11561 				// Don't wrap content if we want selected html
11562 				if (args.selection) {
11563 					args.forced_root_block = '';
11564 				}
11565 
11566 				// Pre process
11567 				if (!args.no_events) {
11568 					args.node = node;
11569 					self.onPreProcess(args);
11570 				}
11571 
11572 				// Setup serializer
11573 				htmlSerializer = new Serializer(settings, schema);
11574 
11575 				// Parse and serialize HTML
11576 				args.content = htmlSerializer.serialize(
11577 					htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)
11578 				);
11579 
11580 				// Replace all BOM characters for now until we can find a better solution
11581 				if (!args.cleanup) {
11582 					args.content = args.content.replace(/\uFEFF/g, '');
11583 				}
11584 
11585 				// Post process
11586 				if (!args.no_events) {
11587 					self.onPostProcess(args);
11588 				}
11589 
11590 				// Restore the old document if it was changed
11591 				if (oldDoc) {
11592 					dom.doc = oldDoc;
11593 				}
11594 
11595 				args.node = null;
11596 
11597 				return args.content;
11598 			},
11599 
11600 			/**
11601 			 * Adds valid elements rules to the serializers schema instance this enables you to specify things
11602 			 * like what elements should be outputted and what attributes specific elements might have.
11603 			 * Consult the Wiki for more details on this format.
11604 			 *
11605 			 * @method addRules
11606 			 * @param {String} rules Valid elements rules string to add to schema.
11607 			 */
11608 			addRules: function(rules) {
11609 				schema.addValidElements(rules);
11610 			},
11611 
11612 			/**
11613 			 * Sets the valid elements rules to the serializers schema instance this enables you to specify things
11614 			 * like what elements should be outputted and what attributes specific elements might have.
11615 			 * Consult the Wiki for more details on this format.
11616 			 *
11617 			 * @method setRules
11618 			 * @param {String} rules Valid elements rules string.
11619 			 */
11620 			setRules: function(rules) {
11621 				schema.setValidElements(rules);
11622 			},
11623 
11624 			onPreProcess: function(args) {
11625 				if (editor) {
11626 					editor.fire('PreProcess', args);
11627 				}
11628 			},
11629 
11630 			onPostProcess: function(args) {
11631 				if (editor) {
11632 					editor.fire('PostProcess', args);
11633 				}
11634 			}
11635 		};
11636 	};
11637 });
11638 
11639 // Included from: js/tinymce/classes/dom/TridentSelection.js
11640 
11641 /**
11642  * TridentSelection.js
11643  *
11644  * Copyright, Moxiecode Systems AB
11645  * Released under LGPL License.
11646  *
11647  * License: http://www.tinymce.com/license
11648  * Contributing: http://www.tinymce.com/contributing
11649  */
11650 
11651 /**
11652  * Selection class for old explorer versions. This one fakes the
11653  * native selection object available on modern browsers.
11654  *
11655  * @class tinymce.dom.TridentSelection
11656  */
11657 define("tinymce/dom/TridentSelection", [], function() {
11658 	function Selection(selection) {
11659 		var self = this, dom = selection.dom, FALSE = false;
11660 
11661 		function getPosition(rng, start) {
11662 			var checkRng, startIndex = 0, endIndex, inside,
11663 				children, child, offset, index, position = -1, parent;
11664 
11665 			// Setup test range, collapse it and get the parent
11666 			checkRng = rng.duplicate();
11667 			checkRng.collapse(start);
11668 			parent = checkRng.parentElement();
11669 
11670 			// Check if the selection is within the right document
11671 			if (parent.ownerDocument !== selection.dom.doc) {
11672 				return;
11673 			}
11674 
11675 			// IE will report non editable elements as it's parent so look for an editable one
11676 			while (parent.contentEditable === "false") {
11677 				parent = parent.parentNode;
11678 			}
11679 
11680 			// If parent doesn't have any children then return that we are inside the element
11681 			if (!parent.hasChildNodes()) {
11682 				return {node: parent, inside: 1};
11683 			}
11684 
11685 			// Setup node list and endIndex
11686 			children = parent.children;
11687 			endIndex = children.length - 1;
11688 
11689 			// Perform a binary search for the position
11690 			while (startIndex <= endIndex) {
11691 				index = Math.floor((startIndex + endIndex) / 2);
11692 
11693 				// Move selection to node and compare the ranges
11694 				child = children[index];
11695 				checkRng.moveToElementText(child);
11696 				position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);
11697 
11698 				// Before/after or an exact match
11699 				if (position > 0) {
11700 					endIndex = index - 1;
11701 				} else if (position < 0) {
11702 					startIndex = index + 1;
11703 				} else {
11704 					return {node: child};
11705 				}
11706 			}
11707 
11708 			// Check if child position is before or we didn't find a position
11709 			if (position < 0) {
11710 				// No element child was found use the parent element and the offset inside that
11711 				if (!child) {
11712 					checkRng.moveToElementText(parent);
11713 					checkRng.collapse(true);
11714 					child = parent;
11715 					inside = true;
11716 				} else {
11717 					checkRng.collapse(false);
11718 				}
11719 
11720 				// Walk character by character in text node until we hit the selected range endpoint,
11721 				// hit the end of document or parent isn't the right one
11722 				// We need to walk char by char since rng.text or rng.htmlText will trim line endings
11723 				offset = 0;
11724 				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
11725 					if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
11726 						break;
11727 					}
11728 
11729 					offset++;
11730 				}
11731 			} else {
11732 				// Child position is after the selection endpoint
11733 				checkRng.collapse(true);
11734 
11735 				// Walk character by character in text node until we hit the selected range endpoint, hit
11736 				// the end of document or parent isn't the right one
11737 				offset = 0;
11738 				while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
11739 					if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
11740 						break;
11741 					}
11742 
11743 					offset++;
11744 				}
11745 			}
11746 
11747 			return {node: child, position: position, offset: offset, inside: inside};
11748 		}
11749 
11750 		// Returns a W3C DOM compatible range object by using the IE Range API
11751 		function getRange() {
11752 			var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark;
11753 
11754 			// If selection is outside the current document just return an empty range
11755 			element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
11756 			if (element.ownerDocument != dom.doc) {
11757 				return domRange;
11758 			}
11759 
11760 			collapsed = selection.isCollapsed();
11761 
11762 			// Handle control selection
11763 			if (ieRange.item) {
11764 				domRange.setStart(element.parentNode, dom.nodeIndex(element));
11765 				domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
11766 
11767 				return domRange;
11768 			}
11769 
11770 			function findEndPoint(start) {
11771 				var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;
11772 
11773 				container = endPoint.node;
11774 				offset = endPoint.offset;
11775 
11776 				if (endPoint.inside && !container.hasChildNodes()) {
11777 					domRange[start ? 'setStart' : 'setEnd'](container, 0);
11778 					return;
11779 				}
11780 
11781 				if (offset === undef) {
11782 					domRange[start ? 'setStartBefore' : 'setEndAfter'](container);
11783 					return;
11784 				}
11785 
11786 				if (endPoint.position < 0) {
11787 					sibling = endPoint.inside ? container.firstChild : container.nextSibling;
11788 
11789 					if (!sibling) {
11790 						domRange[start ? 'setStartAfter' : 'setEndAfter'](container);
11791 						return;
11792 					}
11793 
11794 					if (!offset) {
11795 						if (sibling.nodeType == 3) {
11796 							domRange[start ? 'setStart' : 'setEnd'](sibling, 0);
11797 						} else {
11798 							domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);
11799 						}
11800 
11801 						return;
11802 					}
11803 
11804 					// Find the text node and offset
11805 					while (sibling) {
11806 						nodeValue = sibling.nodeValue;
11807 						textNodeOffset += nodeValue.length;
11808 
11809 						// We are at or passed the position we where looking for
11810 						if (textNodeOffset >= offset) {
11811 							container = sibling;
11812 							textNodeOffset -= offset;
11813 							textNodeOffset = nodeValue.length - textNodeOffset;
11814 							break;
11815 						}
11816 
11817 						sibling = sibling.nextSibling;
11818 					}
11819 				} else {
11820 					// Find the text node and offset
11821 					sibling = container.previousSibling;
11822 
11823 					if (!sibling) {
11824 						return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);
11825 					}
11826 
11827 					// If there isn't any text to loop then use the first position
11828 					if (!offset) {
11829 						if (container.nodeType == 3) {
11830 							domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);
11831 						} else {
11832 							domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);
11833 						}
11834 
11835 						return;
11836 					}
11837 
11838 					while (sibling) {
11839 						textNodeOffset += sibling.nodeValue.length;
11840 
11841 						// We are at or passed the position we where looking for
11842 						if (textNodeOffset >= offset) {
11843 							container = sibling;
11844 							textNodeOffset -= offset;
11845 							break;
11846 						}
11847 
11848 						sibling = sibling.previousSibling;
11849 					}
11850 				}
11851 
11852 				domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);
11853 			}
11854 
11855 			try {
11856 				// Find start point
11857 				findEndPoint(true);
11858 
11859 				// Find end point if needed
11860 				if (!collapsed) {
11861 					findEndPoint();
11862 				}
11863 			} catch (ex) {
11864 				// IE has a nasty bug where text nodes might throw "invalid argument" when you
11865 				// access the nodeValue or other properties of text nodes. This seems to happend when
11866 				// text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.
11867 				if (ex.number == -2147024809) {
11868 					// Get the current selection
11869 					bookmark = self.getBookmark(2);
11870 
11871 					// Get start element
11872 					tmpRange = ieRange.duplicate();
11873 					tmpRange.collapse(true);
11874 					element = tmpRange.parentElement();
11875 
11876 					// Get end element
11877 					if (!collapsed) {
11878 						tmpRange = ieRange.duplicate();
11879 						tmpRange.collapse(false);
11880 						element2 = tmpRange.parentElement();
11881 						element2.innerHTML = element2.innerHTML;
11882 					}
11883 
11884 					// Remove the broken elements
11885 					element.innerHTML = element.innerHTML;
11886 
11887 					// Restore the selection
11888 					self.moveToBookmark(bookmark);
11889 
11890 					// Since the range has moved we need to re-get it
11891 					ieRange = selection.getRng();
11892 
11893 					// Find start point
11894 					findEndPoint(true);
11895 
11896 					// Find end point if needed
11897 					if (!collapsed) {
11898 						findEndPoint();
11899 					}
11900 				} else {
11901 					throw ex; // Throw other errors
11902 				}
11903 			}
11904 
11905 			return domRange;
11906 		}
11907 
11908 		this.getBookmark = function(type) {
11909 			var rng = selection.getRng(), bookmark = {};
11910 
11911 			function getIndexes(node) {
11912 				var parent, root, children, i, indexes = [];
11913 
11914 				parent = node.parentNode;
11915 				root = dom.getRoot().parentNode;
11916 
11917 				while (parent != root && parent.nodeType !== 9) {
11918 					children = parent.children;
11919 
11920 					i = children.length;
11921 					while (i--) {
11922 						if (node === children[i]) {
11923 							indexes.push(i);
11924 							break;
11925 						}
11926 					}
11927 
11928 					node = parent;
11929 					parent = parent.parentNode;
11930 				}
11931 
11932 				return indexes;
11933 			}
11934 
11935 			function getBookmarkEndPoint(start) {
11936 				var position;
11937 
11938 				position = getPosition(rng, start);
11939 				if (position) {
11940 					return {
11941 						position: position.position,
11942 						offset: position.offset,
11943 						indexes: getIndexes(position.node),
11944 						inside: position.inside
11945 					};
11946 				}
11947 			}
11948 
11949 			// Non ubstructive bookmark
11950 			if (type === 2) {
11951 				// Handle text selection
11952 				if (!rng.item) {
11953 					bookmark.start = getBookmarkEndPoint(true);
11954 
11955 					if (!selection.isCollapsed()) {
11956 						bookmark.end = getBookmarkEndPoint();
11957 					}
11958 				} else {
11959 					bookmark.start = {ctrl: true, indexes: getIndexes(rng.item(0))};
11960 				}
11961 			}
11962 
11963 			return bookmark;
11964 		};
11965 
11966 		this.moveToBookmark = function(bookmark) {
11967 			var rng, body = dom.doc.body;
11968 
11969 			function resolveIndexes(indexes) {
11970 				var node, i, idx, children;
11971 
11972 				node = dom.getRoot();
11973 				for (i = indexes.length - 1; i >= 0; i--) {
11974 					children = node.children;
11975 					idx = indexes[i];
11976 
11977 					if (idx <= children.length - 1) {
11978 						node = children[idx];
11979 					}
11980 				}
11981 
11982 				return node;
11983 			}
11984 
11985 			function setBookmarkEndPoint(start) {
11986 				var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef, offset;
11987 
11988 				if (endPoint) {
11989 					moveLeft = endPoint.position > 0;
11990 
11991 					moveRng = body.createTextRange();
11992 					moveRng.moveToElementText(resolveIndexes(endPoint.indexes));
11993 
11994 					offset = endPoint.offset;
11995 					if (offset !== undef) {
11996 						moveRng.collapse(endPoint.inside || moveLeft);
11997 						moveRng.moveStart('character', moveLeft ? -offset : offset);
11998 					} else {
11999 						moveRng.collapse(start);
12000 					}
12001 
12002 					rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);
12003 
12004 					if (start) {
12005 						rng.collapse(true);
12006 					}
12007 				}
12008 			}
12009 
12010 			if (bookmark.start) {
12011 				if (bookmark.start.ctrl) {
12012 					rng = body.createControlRange();
12013 					rng.addElement(resolveIndexes(bookmark.start.indexes));
12014 					rng.select();
12015 				} else {
12016 					rng = body.createTextRange();
12017 					setBookmarkEndPoint(true);
12018 					setBookmarkEndPoint();
12019 					rng.select();
12020 				}
12021 			}
12022 		};
12023 
12024 		this.addRange = function(rng) {
12025 			var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,
12026 				doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;
12027 
12028 			function setEndPoint(start) {
12029 				var container, offset, marker, tmpRng, nodes;
12030 
12031 				marker = dom.create('a');
12032 				container = start ? startContainer : endContainer;
12033 				offset = start ? startOffset : endOffset;
12034 				tmpRng = ieRng.duplicate();
12035 
12036 				if (container == doc || container == doc.documentElement) {
12037 					container = body;
12038 					offset = 0;
12039 				}
12040 
12041 				if (container.nodeType == 3) {
12042 					container.parentNode.insertBefore(marker, container);
12043 					tmpRng.moveToElementText(marker);
12044 					tmpRng.moveStart('character', offset);
12045 					dom.remove(marker);
12046 					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
12047 				} else {
12048 					nodes = container.childNodes;
12049 
12050 					if (nodes.length) {
12051 						if (offset >= nodes.length) {
12052 							dom.insertAfter(marker, nodes[nodes.length - 1]);
12053 						} else {
12054 							container.insertBefore(marker, nodes[offset]);
12055 						}
12056 
12057 						tmpRng.moveToElementText(marker);
12058 					} else if (container.canHaveHTML) {
12059 						// Empty node selection for example <div>|</div>
12060 						// Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open
12061 						container.innerHTML = '<span></span>';
12062 						marker = container.firstChild;
12063 						tmpRng.moveToElementText(marker);
12064 						tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason
12065 					}
12066 
12067 					ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
12068 					dom.remove(marker);
12069 				}
12070 			}
12071 
12072 			// Setup some shorter versions
12073 			startContainer = rng.startContainer;
12074 			startOffset = rng.startOffset;
12075 			endContainer = rng.endContainer;
12076 			endOffset = rng.endOffset;
12077 			ieRng = body.createTextRange();
12078 
12079 			// If single element selection then try making a control selection out of it
12080 			if (startContainer == endContainer && startContainer.nodeType == 1) {
12081 				// Trick to place the caret inside an empty block element like <p></p>
12082 				if (startOffset == endOffset && !startContainer.hasChildNodes()) {
12083 					if (startContainer.canHaveHTML) {
12084 						// Check if previous sibling is an empty block if it is then we need to render it
12085 						// IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
12086 						// Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
12087 						sibling = startContainer.previousSibling;
12088 						if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
12089 							sibling.innerHTML = '';
12090 						} else {
12091 							sibling = null;
12092 						}
12093 
12094 						startContainer.innerHTML = '<span></span><span></span>';
12095 						ieRng.moveToElementText(startContainer.lastChild);
12096 						ieRng.select();
12097 						dom.doc.selection.clear();
12098 						startContainer.innerHTML = '';
12099 
12100 						if (sibling) {
12101 							sibling.innerHTML = '';
12102 						}
12103 						return;
12104 					} else {
12105 						startOffset = dom.nodeIndex(startContainer);
12106 						startContainer = startContainer.parentNode;
12107 					}
12108 				}
12109 
12110 				if (startOffset == endOffset - 1) {
12111 					try {
12112 						ctrlElm = startContainer.childNodes[startOffset];
12113 						ctrlRng = body.createControlRange();
12114 						ctrlRng.addElement(ctrlElm);
12115 						ctrlRng.select();
12116 
12117 						// Check if the range produced is on the correct element and is a control range
12118 						// On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398
12119 						nativeRng = selection.getRng();
12120 						if (nativeRng.item && ctrlElm === nativeRng.item(0)) {
12121 							return;
12122 						}
12123 					} catch (ex) {
12124 						// Ignore
12125 					}
12126 				}
12127 			}
12128 
12129 			// Set start/end point of selection
12130 			setEndPoint(true);
12131 			setEndPoint();
12132 
12133 			// Select the new range and scroll it into view
12134 			ieRng.select();
12135 		};
12136 
12137 		// Expose range method
12138 		this.getRangeAt = getRange;
12139 	}
12140 
12141 	return Selection;
12142 });
12143 
12144 // Included from: js/tinymce/classes/util/VK.js
12145 
12146 /**
12147  * VK.js
12148  *
12149  * Copyright, Moxiecode Systems AB
12150  * Released under LGPL License.
12151  *
12152  * License: http://www.tinymce.com/license
12153  * Contributing: http://www.tinymce.com/contributing
12154  */
12155 
12156 /**
12157  * This file exposes a set of the common KeyCodes for use.  Please grow it as needed.
12158  */
12159 define("tinymce/util/VK", [
12160 	"tinymce/Env"
12161 ], function(Env) {
12162 	return {
12163 		BACKSPACE: 8,
12164 		DELETE: 46,
12165 		DOWN: 40,
12166 		ENTER: 13,
12167 		LEFT: 37,
12168 		RIGHT: 39,
12169 		SPACEBAR: 32,
12170 		TAB: 9,
12171 		UP: 38,
12172 
12173 		modifierPressed: function(e) {
12174 			return e.shiftKey || e.ctrlKey || e.altKey;
12175 		},
12176 
12177 		metaKeyPressed: function(e) {
12178 			// Check if ctrl or meta key is pressed also check if alt is false for Polish users
12179 			return (Env.mac ? e.metaKey : e.ctrlKey) && !e.altKey;
12180 		}
12181 	};
12182 });
12183 
12184 // Included from: js/tinymce/classes/dom/ControlSelection.js
12185 
12186 /**
12187  * ControlSelection.js
12188  *
12189  * Copyright, Moxiecode Systems AB
12190  * Released under LGPL License.
12191  *
12192  * License: http://www.tinymce.com/license
12193  * Contributing: http://www.tinymce.com/contributing
12194  */
12195 
12196 /**
12197  * This class handles control selection of elements. Controls are elements
12198  * that can be resized and needs to be selected as a whole. It adds custom resize handles
12199  * to all browser engines that support properly disabling the built in resize logic.
12200  *
12201  * @class tinymce.dom.ControlSelection
12202  */
12203 define("tinymce/dom/ControlSelection", [
12204 	"tinymce/util/VK",
12205 	"tinymce/util/Tools",
12206 	"tinymce/Env"
12207 ], function(VK, Tools, Env) {
12208 	return function(selection, editor) {
12209 		var dom = editor.dom, each = Tools.each;
12210 		var selectedElm, selectedElmGhost, resizeHandles, selectedHandle, lastMouseDownEvent;
12211 		var startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted;
12212 		var width, height, editableDoc = editor.getDoc(), rootDocument = document, isIE = Env.ie && Env.ie < 11;
12213 
12214 		// Details about each resize handle how to scale etc
12215 		resizeHandles = {
12216 			// Name: x multiplier, y multiplier, delta size x, delta size y
12217 			n:  [0.5,   0,     0,   -1],
12218 			e:  [1,    0.5,    1,    0],
12219 			s:  [0.5,   1,     0,    1],
12220 			w:  [0,    0.5,   -1,    0],
12221 			nw: [0,     0,    -1,   -1],
12222 			ne: [1,     0,     1,   -1],
12223 			se: [1,     1,     1,    1],
12224 			sw: [0,     1,    -1,    1]
12225 		};
12226 
12227 		// Add CSS for resize handles, cloned element and selected
12228 		var rootClass = '.mce-content-body';
12229 		editor.contentStyles.push(
12230 			rootClass + ' div.mce-resizehandle {' +
12231 				'position: absolute;' +
12232 				'border: 1px solid black;' +
12233 				'background: #FFF;' +
12234 				'width: 5px;' +
12235 				'height: 5px;' +
12236 				'z-index: 10000' +
12237 			'}' +
12238 			rootClass + ' .mce-resizehandle:hover {' +
12239 				'background: #000' +
12240 			'}' +
12241 			rootClass + ' img[data-mce-selected], hr[data-mce-selected] {' +
12242 				'outline: 1px solid black;' +
12243 				'resize: none' + // Have been talks about implementing this in browsers
12244 			'}' +
12245 			rootClass + ' .mce-clonedresizable {' +
12246 				'position: absolute;' +
12247 				(Env.gecko ? '' : 'outline: 1px dashed black;') + // Gecko produces trails while resizing
12248 				'opacity: .5;' +
12249 				'filter: alpha(opacity=50);' +
12250 				'z-index: 10000' +
12251 			'}'
12252 		);
12253 
12254 		function isResizable(elm) {
12255 			var selector = editor.settings.object_resizing;
12256 
12257 			if (selector === false || Env.iOS) {
12258 				return false;
12259 			}
12260 
12261 			if (typeof selector != 'string') {
12262 				selector = 'table,img,div';
12263 			}
12264 
12265 			if (elm.getAttribute('data-mce-resize') === 'false') {
12266 				return false;
12267 			}
12268 
12269 			return editor.dom.is(elm, selector);
12270 		}
12271 
12272 		function resizeGhostElement(e) {
12273 			var deltaX, deltaY;
12274 
12275 			// Calc new width/height
12276 			deltaX = e.screenX - startX;
12277 			deltaY = e.screenY - startY;
12278 
12279 			// Calc new size
12280 			width = deltaX * selectedHandle[2] + startW;
12281 			height = deltaY * selectedHandle[3] + startH;
12282 
12283 			// Never scale down lower than 5 pixels
12284 			width = width < 5 ? 5 : width;
12285 			height = height < 5 ? 5 : height;
12286 
12287 			// Constrain proportions when modifier key is pressed or if the nw, ne, sw, se corners are moved on an image
12288 			if (VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0)) {
12289 				width = Math.round(height / ratio);
12290 				height = Math.round(width * ratio);
12291 			}
12292 
12293 			// Update ghost size
12294 			dom.setStyles(selectedElmGhost, {
12295 				width: width,
12296 				height: height
12297 			});
12298 
12299 			// Update ghost X position if needed
12300 			if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {
12301 				dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));
12302 			}
12303 
12304 			// Update ghost Y position if needed
12305 			if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {
12306 				dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));
12307 			}
12308 
12309 			if (!resizeStarted) {
12310 				editor.fire('ObjectResizeStart', {target: selectedElm, width: startW, height: startH});
12311 				resizeStarted = true;
12312 			}
12313 		}
12314 
12315 		function endGhostResize() {
12316 			resizeStarted = false;
12317 
12318 			function setSizeProp(name, value) {
12319 				if (value) {
12320 					// Resize by using style or attribute
12321 					if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) {
12322 						dom.setStyle(selectedElm, name, value);
12323 					} else {
12324 						dom.setAttrib(selectedElm, name, value);
12325 					}
12326 				}
12327 			}
12328 
12329 			// Set width/height properties
12330 			setSizeProp('width', width);
12331 			setSizeProp('height', height);
12332 
12333 			dom.unbind(editableDoc, 'mousemove', resizeGhostElement);
12334 			dom.unbind(editableDoc, 'mouseup', endGhostResize);
12335 
12336 			if (rootDocument != editableDoc) {
12337 				dom.unbind(rootDocument, 'mousemove', resizeGhostElement);
12338 				dom.unbind(rootDocument, 'mouseup', endGhostResize);
12339 			}
12340 
12341 			// Remove ghost and update resize handle positions
12342 			dom.remove(selectedElmGhost);
12343 
12344 			if (!isIE || selectedElm.nodeName == "TABLE") {
12345 				showResizeRect(selectedElm);
12346 			}
12347 
12348 			editor.fire('ObjectResized', {target: selectedElm, width: width, height: height});
12349 			editor.nodeChanged();
12350 		}
12351 
12352 		function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) {
12353 			var position, targetWidth, targetHeight, e, rect, offsetParent = editor.getBody();
12354 
12355 			unbindResizeHandleEvents();
12356 
12357 			// Get position and size of target
12358 			position = dom.getPos(targetElm, offsetParent);
12359 			selectedElmX = position.x;
12360 			selectedElmY = position.y;
12361 			rect = targetElm.getBoundingClientRect(); // Fix for Gecko offsetHeight for table with caption
12362 			targetWidth = rect.width || (rect.right - rect.left);
12363 			targetHeight = rect.height || (rect.bottom - rect.top);
12364 
12365 			// Reset width/height if user selects a new image/table
12366 			if (selectedElm != targetElm) {
12367 				detachResizeStartListener();
12368 				selectedElm = targetElm;
12369 				width = height = 0;
12370 			}
12371 
12372 			// Makes it possible to disable resizing
12373 			e = editor.fire('ObjectSelected', {target: targetElm});
12374 
12375 			if (isResizable(targetElm) && !e.isDefaultPrevented()) {
12376 				each(resizeHandles, function(handle, name) {
12377 					var handleElm, handlerContainerElm;
12378 
12379 					function startDrag(e) {
12380 						startX = e.screenX;
12381 						startY = e.screenY;
12382 						startW = selectedElm.clientWidth;
12383 						startH = selectedElm.clientHeight;
12384 						ratio = startH / startW;
12385 						selectedHandle = handle;
12386 
12387 						selectedElmGhost = selectedElm.cloneNode(true);
12388 						dom.addClass(selectedElmGhost, 'mce-clonedresizable');
12389 						selectedElmGhost.contentEditable = false; // Hides IE move layer cursor
12390 						selectedElmGhost.unSelectabe = true;
12391 						dom.setStyles(selectedElmGhost, {
12392 							left: selectedElmX,
12393 							top: selectedElmY,
12394 							margin: 0
12395 						});
12396 
12397 						selectedElmGhost.removeAttribute('data-mce-selected');
12398 						editor.getBody().appendChild(selectedElmGhost);
12399 
12400 						dom.bind(editableDoc, 'mousemove', resizeGhostElement);
12401 						dom.bind(editableDoc, 'mouseup', endGhostResize);
12402 
12403 						if (rootDocument != editableDoc) {
12404 							dom.bind(rootDocument, 'mousemove', resizeGhostElement);
12405 							dom.bind(rootDocument, 'mouseup', endGhostResize);
12406 						}
12407 					}
12408 
12409 					if (mouseDownHandleName) {
12410 						// Drag started by IE native resizestart
12411 						if (name == mouseDownHandleName) {
12412 							startDrag(mouseDownEvent);
12413 						}
12414 
12415 						return;
12416 					}
12417 
12418 					// Get existing or render resize handle
12419 					handleElm = dom.get('mceResizeHandle' + name);
12420 					if (!handleElm) {
12421 						handlerContainerElm = editor.getBody();
12422 
12423 						handleElm = dom.add(handlerContainerElm, 'div', {
12424 							id: 'mceResizeHandle' + name,
12425 							'data-mce-bogus': true,
12426 							'class': 'mce-resizehandle',
12427 							unselectable: true,
12428 							style: 'cursor:' + name + '-resize; margin:0; padding:0'
12429 						});
12430 
12431 						// Hides IE move layer cursor
12432 						// If we set it on Chrome we get this wounderful bug: #6725
12433 						if (Env.ie) {
12434 							handleElm.contentEditable = false;
12435 						}
12436 					} else {
12437 						dom.show(handleElm);
12438 					}
12439 
12440 					if (!handle.elm) {
12441 						dom.bind(handleElm, 'mousedown', function(e) {
12442 							e.stopImmediatePropagation();
12443 							e.preventDefault();
12444 							startDrag(e);
12445 						});
12446 
12447 						handle.elm = handleElm;
12448 					}
12449 
12450 					/*
12451 					var halfHandleW = handleElm.offsetWidth / 2;
12452 					var halfHandleH = handleElm.offsetHeight / 2;
12453 
12454 					// Position element
12455 					dom.setStyles(handleElm, {
12456 						left: Math.floor((targetWidth * handle[0] + selectedElmX) - halfHandleW + (handle[2] * halfHandleW)),
12457 						top: Math.floor((targetHeight * handle[1] + selectedElmY) - halfHandleH + (handle[3] * halfHandleH))
12458 					});
12459 					*/
12460 
12461 					// Position element
12462 					dom.setStyles(handleElm, {
12463 						left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2),
12464 						top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2)
12465 					});
12466 				});
12467 			} else {
12468 				hideResizeRect();
12469 			}
12470 
12471 			selectedElm.setAttribute('data-mce-selected', '1');
12472 		}
12473 
12474 		function hideResizeRect() {
12475 			var name, handleElm;
12476 
12477 			unbindResizeHandleEvents();
12478 
12479 			if (selectedElm) {
12480 				selectedElm.removeAttribute('data-mce-selected');
12481 			}
12482 
12483 			for (name in resizeHandles) {
12484 				handleElm = dom.get('mceResizeHandle' + name);
12485 				if (handleElm) {
12486 					dom.unbind(handleElm);
12487 					dom.remove(handleElm);
12488 				}
12489 			}
12490 		}
12491 
12492 		function updateResizeRect(e) {
12493 			var controlElm;
12494 
12495 			function isChildOrEqual(node, parent) {
12496 				if (node) {
12497 					do {
12498 						if (node === parent) {
12499 							return true;
12500 						}
12501 					} while ((node = node.parentNode));
12502 				}
12503 			}
12504 
12505 			// Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v
12506 			each(dom.select('img[data-mce-selected],hr[data-mce-selected]'), function(img) {
12507 				img.removeAttribute('data-mce-selected');
12508 			});
12509 
12510 			controlElm = e.type == 'mousedown' ? e.target : selection.getNode();
12511 			controlElm = dom.getParent(controlElm, isIE ? 'table' : 'table,img,hr');
12512 
12513 			if (isChildOrEqual(controlElm, editor.getBody())) {
12514 				disableGeckoResize();
12515 
12516 				if (isChildOrEqual(selection.getStart(), controlElm) && isChildOrEqual(selection.getEnd(), controlElm)) {
12517 					if (!isIE || (controlElm != selection.getStart() && selection.getStart().nodeName !== 'IMG')) {
12518 						showResizeRect(controlElm);
12519 						return;
12520 					}
12521 				}
12522 			}
12523 
12524 			hideResizeRect();
12525 		}
12526 
12527 		function attachEvent(elm, name, func) {
12528 			if (elm && elm.attachEvent) {
12529 				elm.attachEvent('on' + name, func);
12530 			}
12531 		}
12532 
12533 		function detachEvent(elm, name, func) {
12534 			if (elm && elm.detachEvent) {
12535 				elm.detachEvent('on' + name, func);
12536 			}
12537 		}
12538 
12539 		function resizeNativeStart(e) {
12540 			var target = e.srcElement, pos, name, corner, cornerX, cornerY, relativeX, relativeY;
12541 
12542 			pos = target.getBoundingClientRect();
12543 			relativeX = lastMouseDownEvent.clientX - pos.left;
12544 			relativeY = lastMouseDownEvent.clientY - pos.top;
12545 
12546 			// Figure out what corner we are draging on
12547 			for (name in resizeHandles) {
12548 				corner = resizeHandles[name];
12549 
12550 				cornerX = target.offsetWidth * corner[0];
12551 				cornerY = target.offsetHeight * corner[1];
12552 
12553 				if (Math.abs(cornerX - relativeX) < 8 && Math.abs(cornerY - relativeY) < 8) {
12554 					selectedHandle = corner;
12555 					break;
12556 				}
12557 			}
12558 
12559 			// Remove native selection and let the magic begin
12560 			resizeStarted = true;
12561 			editor.getDoc().selection.empty();
12562 			showResizeRect(target, name, lastMouseDownEvent);
12563 		}
12564 
12565 		function nativeControlSelect(e) {
12566 			var target = e.srcElement;
12567 
12568 			if (target != selectedElm) {
12569 				detachResizeStartListener();
12570 
12571 				if (target.id.indexOf('mceResizeHandle') === 0) {
12572 					e.returnValue = false;
12573 					return;
12574 				}
12575 
12576 				if (target.nodeName == 'IMG' || target.nodeName == 'TABLE') {
12577 					hideResizeRect();
12578 					selectedElm = target;
12579 					attachEvent(target, 'resizestart', resizeNativeStart);
12580 				}
12581 			}
12582 		}
12583 
12584 		function detachResizeStartListener() {
12585 			detachEvent(selectedElm, 'resizestart', resizeNativeStart);
12586 		}
12587 
12588 		function unbindResizeHandleEvents() {
12589 			for (var name in resizeHandles) {
12590 				var handle = resizeHandles[name];
12591 
12592 				if (handle.elm) {
12593 					dom.unbind(handle.elm);
12594 					delete handle.elm;
12595 				}
12596 			}
12597 		}
12598 
12599 		function disableGeckoResize() {
12600 			try {
12601 				// Disable object resizing on Gecko
12602 				editor.getDoc().execCommand('enableObjectResizing', false, false);
12603 			} catch (ex) {
12604 				// Ignore
12605 			}
12606 		}
12607 
12608 		function controlSelect(elm) {
12609 			var ctrlRng;
12610 
12611 			if (!isIE) {
12612 				return;
12613 			}
12614 
12615 			ctrlRng = editableDoc.body.createControlRange();
12616 
12617 			try {
12618 				ctrlRng.addElement(elm);
12619 				ctrlRng.select();
12620 				return true;
12621 			} catch (ex) {
12622 				// Ignore since the element can't be control selected for example a P tag
12623 			}
12624 		}
12625 
12626 		editor.on('init', function() {
12627 			if (isIE) {
12628 				// Hide the resize rect on resize and reselect the image
12629 				editor.on('ObjectResized', function(e) {
12630 					if (e.target.nodeName != 'TABLE') {
12631 						hideResizeRect();
12632 						controlSelect(e.target);
12633 					}
12634 				});
12635 
12636 				attachEvent(editor.getBody(), 'controlselect', nativeControlSelect);
12637 
12638 				editor.on('mousedown', function(e) {
12639 					lastMouseDownEvent = e;
12640 				});
12641 			} else {
12642 				disableGeckoResize();
12643 
12644 				if (Env.ie >= 11) {
12645 					// TODO: Drag/drop doesn't work
12646 					editor.on('mouseup', function(e) {
12647 						var nodeName = e.target.nodeName;
12648 
12649 						if (/^(TABLE|IMG|HR)$/.test(nodeName)) {
12650 							editor.selection.select(e.target, nodeName == 'TABLE');
12651 							editor.nodeChanged();
12652 						}
12653 					});
12654 
12655 					editor.dom.bind(editor.getBody(), 'mscontrolselect', function(e) {
12656 						if (/^(TABLE|IMG|HR)$/.test(e.target.nodeName)) {
12657 							e.preventDefault();
12658 
12659 							// This moves the selection from being a control selection to a text like selection like in WebKit #6753
12660 							// TODO: Fix this the day IE works like other browsers without this nasty native ugly control selections.
12661 							if (e.target.tagName == 'IMG') {
12662 								window.setTimeout(function() {
12663 									editor.selection.select(e.target);
12664 								}, 0);
12665 							}
12666 						}
12667 					});
12668 				}
12669 			}
12670 
12671 			editor.on('nodechange mousedown mouseup ResizeEditor', updateResizeRect);
12672 
12673 			// Update resize rect while typing in a table
12674 			editor.on('keydown keyup', function(e) {
12675 				if (selectedElm && selectedElm.nodeName == "TABLE") {
12676 					updateResizeRect(e);
12677 				}
12678 			});
12679 
12680 			editor.on('hide', hideResizeRect);
12681 
12682 			// Hide rect on focusout since it would float on top of windows otherwise
12683 			//editor.on('focusout', hideResizeRect);
12684 		});
12685 
12686 		editor.on('remove', unbindResizeHandleEvents);
12687 
12688 		function destroy() {
12689 			selectedElm = selectedElmGhost = null;
12690 
12691 			if (isIE) {
12692 				detachResizeStartListener();
12693 				detachEvent(editor.getBody(), 'controlselect', nativeControlSelect);
12694 			}
12695 		}
12696 
12697 		return {
12698 			isResizable: isResizable,
12699 			showResizeRect: showResizeRect,
12700 			hideResizeRect: hideResizeRect,
12701 			updateResizeRect: updateResizeRect,
12702 			controlSelect: controlSelect,
12703 			destroy: destroy
12704 		};
12705 	};
12706 });
12707 
12708 // Included from: js/tinymce/classes/dom/RangeUtils.js
12709 
12710 /**
12711  * Range.js
12712  *
12713  * Copyright, Moxiecode Systems AB
12714  * Released under LGPL License.
12715  *
12716  * License: http://www.tinymce.com/license
12717  * Contributing: http://www.tinymce.com/contributing
12718  */
12719 
12720 /**
12721  * RangeUtils
12722  *
12723  * @class tinymce.dom.RangeUtils
12724  * @private
12725  */
12726 define("tinymce/dom/RangeUtils", [
12727 	"tinymce/util/Tools",
12728 	"tinymce/dom/TreeWalker"
12729 ], function(Tools, TreeWalker) {
12730 	var each = Tools.each;
12731 
12732 	function RangeUtils(dom) {
12733 		/**
12734 		 * Walks the specified range like object and executes the callback for each sibling collection it finds.
12735 		 *
12736 		 * @method walk
12737 		 * @param {Object} rng Range like object.
12738 		 * @param {function} callback Callback function to execute for each sibling collection.
12739 		 */
12740 		this.walk = function(rng, callback) {
12741 			var startContainer = rng.startContainer,
12742 				startOffset = rng.startOffset,
12743 				endContainer = rng.endContainer,
12744 				endOffset = rng.endOffset,
12745 				ancestor, startPoint,
12746 				endPoint, node, parent, siblings, nodes;
12747 
12748 			// Handle table cell selection the table plugin enables
12749 			// you to fake select table cells and perform formatting actions on them
12750 			nodes = dom.select('td.mce-item-selected,th.mce-item-selected');
12751 			if (nodes.length > 0) {
12752 				each(nodes, function(node) {
12753 					callback([node]);
12754 				});
12755 
12756 				return;
12757 			}
12758 
12759 			/**
12760 			 * Excludes start/end text node if they are out side the range
12761 			 *
12762 			 * @private
12763 			 * @param {Array} nodes Nodes to exclude items from.
12764 			 * @return {Array} Array with nodes excluding the start/end container if needed.
12765 			 */
12766 			function exclude(nodes) {
12767 				var node;
12768 
12769 				// First node is excluded
12770 				node = nodes[0];
12771 				if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {
12772 					nodes.splice(0, 1);
12773 				}
12774 
12775 				// Last node is excluded
12776 				node = nodes[nodes.length - 1];
12777 				if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {
12778 					nodes.splice(nodes.length - 1, 1);
12779 				}
12780 
12781 				return nodes;
12782 			}
12783 
12784 			/**
12785 			 * Collects siblings
12786 			 *
12787 			 * @private
12788 			 * @param {Node} node Node to collect siblings from.
12789 			 * @param {String} name Name of the sibling to check for.
12790 			 * @return {Array} Array of collected siblings.
12791 			 */
12792 			function collectSiblings(node, name, end_node) {
12793 				var siblings = [];
12794 
12795 				for (; node && node != end_node; node = node[name]) {
12796 					siblings.push(node);
12797 				}
12798 
12799 				return siblings;
12800 			}
12801 
12802 			/**
12803 			 * Find an end point this is the node just before the common ancestor root.
12804 			 *
12805 			 * @private
12806 			 * @param {Node} node Node to start at.
12807 			 * @param {Node} root Root/ancestor element to stop just before.
12808 			 * @return {Node} Node just before the root element.
12809 			 */
12810 			function findEndPoint(node, root) {
12811 				do {
12812 					if (node.parentNode == root) {
12813 						return node;
12814 					}
12815 
12816 					node = node.parentNode;
12817 				} while(node);
12818 			}
12819 
12820 			function walkBoundary(start_node, end_node, next) {
12821 				var siblingName = next ? 'nextSibling' : 'previousSibling';
12822 
12823 				for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {
12824 					parent = node.parentNode;
12825 					siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);
12826 
12827 					if (siblings.length) {
12828 						if (!next) {
12829 							siblings.reverse();
12830 						}
12831 
12832 						callback(exclude(siblings));
12833 					}
12834 				}
12835 			}
12836 
12837 			// If index based start position then resolve it
12838 			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
12839 				startContainer = startContainer.childNodes[startOffset];
12840 			}
12841 
12842 			// If index based end position then resolve it
12843 			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
12844 				endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];
12845 			}
12846 
12847 			// Same container
12848 			if (startContainer == endContainer) {
12849 				return callback(exclude([startContainer]));
12850 			}
12851 
12852 			// Find common ancestor and end points
12853 			ancestor = dom.findCommonAncestor(startContainer, endContainer);
12854 
12855 			// Process left side
12856 			for (node = startContainer; node; node = node.parentNode) {
12857 				if (node === endContainer) {
12858 					return walkBoundary(startContainer, ancestor, true);
12859 				}
12860 
12861 				if (node === ancestor) {
12862 					break;
12863 				}
12864 			}
12865 
12866 			// Process right side
12867 			for (node = endContainer; node; node = node.parentNode) {
12868 				if (node === startContainer) {
12869 					return walkBoundary(endContainer, ancestor);
12870 				}
12871 
12872 				if (node === ancestor) {
12873 					break;
12874 				}
12875 			}
12876 
12877 			// Find start/end point
12878 			startPoint = findEndPoint(startContainer, ancestor) || startContainer;
12879 			endPoint = findEndPoint(endContainer, ancestor) || endContainer;
12880 
12881 			// Walk left leaf
12882 			walkBoundary(startContainer, startPoint, true);
12883 
12884 			// Walk the middle from start to end point
12885 			siblings = collectSiblings(
12886 				startPoint == startContainer ? startPoint : startPoint.nextSibling,
12887 				'nextSibling',
12888 				endPoint == endContainer ? endPoint.nextSibling : endPoint
12889 			);
12890 
12891 			if (siblings.length) {
12892 				callback(exclude(siblings));
12893 			}
12894 
12895 			// Walk right leaf
12896 			walkBoundary(endContainer, endPoint);
12897 		};
12898 
12899 		/**
12900 		 * Splits the specified range at it's start/end points.
12901 		 *
12902 		 * @private
12903 		 * @param {Range/RangeObject} rng Range to split.
12904 		 * @return {Object} Range position object.
12905 		 */
12906 		this.split = function(rng) {
12907 			var startContainer = rng.startContainer,
12908 				startOffset = rng.startOffset,
12909 				endContainer = rng.endContainer,
12910 				endOffset = rng.endOffset;
12911 
12912 			function splitText(node, offset) {
12913 				return node.splitText(offset);
12914 			}
12915 
12916 			// Handle single text node
12917 			if (startContainer == endContainer && startContainer.nodeType == 3) {
12918 				if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {
12919 					endContainer = splitText(startContainer, startOffset);
12920 					startContainer = endContainer.previousSibling;
12921 
12922 					if (endOffset > startOffset) {
12923 						endOffset = endOffset - startOffset;
12924 						startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;
12925 						endOffset = endContainer.nodeValue.length;
12926 						startOffset = 0;
12927 					} else {
12928 						endOffset = 0;
12929 					}
12930 				}
12931 			} else {
12932 				// Split startContainer text node if needed
12933 				if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {
12934 					startContainer = splitText(startContainer, startOffset);
12935 					startOffset = 0;
12936 				}
12937 
12938 				// Split endContainer text node if needed
12939 				if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {
12940 					endContainer = splitText(endContainer, endOffset).previousSibling;
12941 					endOffset = endContainer.nodeValue.length;
12942 				}
12943 			}
12944 
12945 			return {
12946 				startContainer: startContainer,
12947 				startOffset: startOffset,
12948 				endContainer: endContainer,
12949 				endOffset: endOffset
12950 			};
12951 		};
12952 
12953 		/**
12954 		 * Normalizes the specified range by finding the closest best suitable caret location.
12955 		 *
12956 		 * @private
12957 		 * @param {Range} rng Range to normalize.
12958 		 * @return {Boolean} True/false if the specified range was normalized or not.
12959 		 */
12960 		this.normalize = function(rng) {
12961 			var normalized, collapsed;
12962 
12963 			function normalizeEndPoint(start) {
12964 				var container, offset, walker, body = dom.getRoot(), node, nonEmptyElementsMap;
12965 				var directionLeft, isAfterNode;
12966 
12967 				function hasBrBeforeAfter(node, left) {
12968 					var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
12969 
12970 					while ((node = walker[left ? 'prev' : 'next']())) {
12971 						if (node.nodeName === "BR") {
12972 							return true;
12973 						}
12974 					}
12975 				}
12976 
12977 				function isPrevNode(node, name) {
12978 					return node.previousSibling && node.previousSibling.nodeName == name;
12979 				}
12980 
12981 				// Walks the dom left/right to find a suitable text node to move the endpoint into
12982 				// It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG
12983 				function findTextNodeRelative(left, startNode) {
12984 					var walker, lastInlineElement, parentBlockContainer;
12985 
12986 					startNode = startNode || container;
12987 					parentBlockContainer = dom.getParent(startNode.parentNode, dom.isBlock) || body;
12988 
12989 					// Lean left before the BR element if it's the only BR within a block element. Gecko bug: #6680
12990 					// This: <p><br>|</p> becomes <p>|<br></p>
12991 					if (left && startNode.nodeName == 'BR' && isAfterNode && dom.isEmpty(parentBlockContainer)) {
12992 						container = startNode.parentNode;
12993 						offset = dom.nodeIndex(startNode);
12994 						normalized = true;
12995 						return;
12996 					}
12997 
12998 					// Walk left until we hit a text node we can move to or a block/br/img
12999 					walker = new TreeWalker(startNode, parentBlockContainer);
13000 					while ((node = walker[left ? 'prev' : 'next']())) {
13001 						// Break if we hit a non content editable node
13002 						if (dom.getContentEditableParent(node) === "false") {
13003 							return;
13004 						}
13005 
13006 						// Found text node that has a length
13007 						if (node.nodeType === 3 && node.nodeValue.length > 0) {
13008 							container = node;
13009 							offset = left ? node.nodeValue.length : 0;
13010 							normalized = true;
13011 							return;
13012 						}
13013 
13014 						// Break if we find a block or a BR/IMG/INPUT etc
13015 						if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
13016 							return;
13017 						}
13018 
13019 						lastInlineElement = node;
13020 					}
13021 
13022 					// Only fetch the last inline element when in caret mode for now
13023 					if (collapsed && lastInlineElement) {
13024 						container = lastInlineElement;
13025 						normalized = true;
13026 						offset = 0;
13027 					}
13028 				}
13029 
13030 				container = rng[(start ? 'start' : 'end') + 'Container'];
13031 				offset = rng[(start ? 'start' : 'end') + 'Offset'];
13032 				isAfterNode = container.nodeType == 1 && offset === container.childNodes.length;
13033 				nonEmptyElementsMap = dom.schema.getNonEmptyElements();
13034 				directionLeft = start;
13035 
13036 				if (container.nodeType == 1 && offset > container.childNodes.length - 1) {
13037 					directionLeft = false;
13038 				}
13039 
13040 				// If the container is a document move it to the body element
13041 				if (container.nodeType === 9) {
13042 					container = dom.getRoot();
13043 					offset = 0;
13044 				}
13045 
13046 				// If the container is body try move it into the closest text node or position
13047 				if (container === body) {
13048 					// If start is before/after a image, table etc
13049 					if (directionLeft) {
13050 						node = container.childNodes[offset > 0 ? offset - 1 : 0];
13051 						if (node) {
13052 							if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {
13053 								return;
13054 							}
13055 						}
13056 					}
13057 
13058 					// Resolve the index
13059 					if (container.hasChildNodes()) {
13060 						offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);
13061 						container = container.childNodes[offset];
13062 						offset = 0;
13063 
13064 						// Don't walk into elements that doesn't have any child nodes like a IMG
13065 						if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) {
13066 							// Walk the DOM to find a text node to place the caret at or a BR
13067 							node = container;
13068 							walker = new TreeWalker(container, body);
13069 
13070 							do {
13071 								// Found a text node use that position
13072 								if (node.nodeType === 3 && node.nodeValue.length > 0) {
13073 									offset = directionLeft ? 0 : node.nodeValue.length;
13074 									container = node;
13075 									normalized = true;
13076 									break;
13077 								}
13078 
13079 								// Found a BR/IMG element that we can place the caret before
13080 								if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
13081 									offset = dom.nodeIndex(node);
13082 									container = node.parentNode;
13083 
13084 									// Put caret after image when moving the end point
13085 									if (node.nodeName ==  "IMG" && !directionLeft) {
13086 										offset++;
13087 									}
13088 
13089 									normalized = true;
13090 									break;
13091 								}
13092 							} while ((node = (directionLeft ? walker.next() : walker.prev())));
13093 						}
13094 					}
13095 				}
13096 
13097 				// Lean the caret to the left if possible
13098 				if (collapsed) {
13099 					// So this: <b>x</b><i>|x</i>
13100 					// Becomes: <b>x|</b><i>x</i>
13101 					// Seems that only gecko has issues with this
13102 					if (container.nodeType === 3 && offset === 0) {
13103 						findTextNodeRelative(true);
13104 					}
13105 
13106 					// Lean left into empty inline elements when the caret is before a BR
13107 					// So this: <i><b></b><i>|<br></i>
13108 					// Becomes: <i><b>|</b><i><br></i>
13109 					// Seems that only gecko has issues with this.
13110 					// Special edge case for <p><a>x</a>|<br></p> since we don't want <p><a>x|</a><br></p>
13111 					if (container.nodeType === 1) {
13112 						node = container.childNodes[offset];
13113 
13114 						// Offset is after the containers last child
13115 						// then use the previous child for normalization
13116 						if (!node) {
13117 							node = container.childNodes[offset - 1];
13118 						}
13119 
13120 						if (node && node.nodeName === 'BR' && !isPrevNode(node, 'A') &&
13121 							!hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {
13122 							findTextNodeRelative(true, node);
13123 						}
13124 					}
13125 				}
13126 
13127 				// Lean the start of the selection right if possible
13128 				// So this: x[<b>x]</b>
13129 				// Becomes: x<b>[x]</b>
13130 				if (directionLeft && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
13131 					findTextNodeRelative(false);
13132 				}
13133 
13134 				// Set endpoint if it was normalized
13135 				if (normalized) {
13136 					rng['set' + (start ? 'Start' : 'End')](container, offset);
13137 				}
13138 			}
13139 
13140 			collapsed = rng.collapsed;
13141 
13142 			normalizeEndPoint(true);
13143 
13144 			if (!collapsed) {
13145 				normalizeEndPoint();
13146 			}
13147 
13148 			// If it was collapsed then make sure it still is
13149 			if (normalized && collapsed) {
13150 				rng.collapse(true);
13151 			}
13152 
13153 			return normalized;
13154 		};
13155 	}
13156 
13157 	/**
13158 	 * Compares two ranges and checks if they are equal.
13159 	 *
13160 	 * @static
13161 	 * @method compareRanges
13162 	 * @param {DOMRange} rng1 First range to compare.
13163 	 * @param {DOMRange} rng2 First range to compare.
13164 	 * @return {Boolean} true/false if the ranges are equal.
13165 	 */
13166 	RangeUtils.compareRanges = function(rng1, rng2) {
13167 		if (rng1 && rng2) {
13168 			// Compare native IE ranges
13169 			if (rng1.item || rng1.duplicate) {
13170 				// Both are control ranges and the selected element matches
13171 				if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) {
13172 					return true;
13173 				}
13174 
13175 				// Both are text ranges and the range matches
13176 				if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {
13177 					return true;
13178 				}
13179 			} else {
13180 				// Compare w3c ranges
13181 				return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
13182 			}
13183 		}
13184 
13185 		return false;
13186 	};
13187 
13188 	return RangeUtils;
13189 });
13190 
13191 // Included from: js/tinymce/classes/dom/Selection.js
13192 
13193 /**
13194  * Selection.js
13195  *
13196  * Copyright, Moxiecode Systems AB
13197  * Released under LGPL License.
13198  *
13199  * License: http://www.tinymce.com/license
13200  * Contributing: http://www.tinymce.com/contributing
13201  */
13202 
13203 /**
13204  * This class handles text and control selection it's an crossbrowser utility class.
13205  * Consult the TinyMCE Wiki API for more details and examples on how to use this class.
13206  *
13207  * @class tinymce.dom.Selection
13208  * @example
13209  * // Getting the currently selected node for the active editor
13210  * alert(tinymce.activeEditor.selection.getNode().nodeName);
13211  */
13212 define("tinymce/dom/Selection", [
13213 	"tinymce/dom/TreeWalker",
13214 	"tinymce/dom/TridentSelection",
13215 	"tinymce/dom/ControlSelection",
13216 	"tinymce/dom/RangeUtils",
13217 	"tinymce/Env",
13218 	"tinymce/util/Tools"
13219 ], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, Env, Tools) {
13220 	var each = Tools.each, grep = Tools.grep, trim = Tools.trim;
13221 	var isIE = Env.ie, isOpera = Env.opera;
13222 
13223 	/**
13224 	 * Constructs a new selection instance.
13225 	 *
13226 	 * @constructor
13227 	 * @method Selection
13228 	 * @param {tinymce.dom.DOMUtils} dom DOMUtils object reference.
13229 	 * @param {Window} win Window to bind the selection object to.
13230 	 * @param {tinymce.dom.Serializer} serializer DOM serialization class to use for getContent.
13231 	 */
13232 	function Selection(dom, win, serializer, editor) {
13233 		var self = this;
13234 
13235 		self.dom = dom;
13236 		self.win = win;
13237 		self.serializer = serializer;
13238 		self.editor = editor;
13239 
13240 		self.controlSelection = new ControlSelection(self, editor);
13241 
13242 		// No W3C Range support
13243 		if (!self.win.getSelection) {
13244 			self.tridentSel = new TridentSelection(self);
13245 		}
13246 	}
13247 
13248 	Selection.prototype = {
13249 		/**
13250 		 * Move the selection cursor range to the specified node and offset.
13251 		 * If there is no node specified it will move it to the first suitable location within the body.
13252 		 *
13253 		 * @method setCursorLocation
13254 		 * @param {Node} node Optional node to put the cursor in.
13255 		 * @param {Number} offset Optional offset from the start of the node to put the cursor at.
13256 		 */
13257 		setCursorLocation: function(node, offset) {
13258 			var self = this, rng = self.dom.createRng();
13259 
13260 			if (!node) {
13261 				self._moveEndPoint(rng, self.editor.getBody(), true);
13262 				self.setRng(rng);
13263 			} else {
13264 				rng.setStart(node, offset);
13265 				rng.setEnd(node, offset);
13266 				self.setRng(rng);
13267 				self.collapse(false);
13268 			}
13269 		},
13270 
13271 		/**
13272 		 * Returns the selected contents using the DOM serializer passed in to this class.
13273 		 *
13274 		 * @method getContent
13275 		 * @param {Object} s Optional settings class with for example output format text or html.
13276 		 * @return {String} Selected contents in for example HTML format.
13277 		 * @example
13278 		 * // Alerts the currently selected contents
13279 		 * alert(tinymce.activeEditor.selection.getContent());
13280 		 *
13281 		 * // Alerts the currently selected contents as plain text
13282 		 * alert(tinymce.activeEditor.selection.getContent({format: 'text'}));
13283 		 */
13284 		getContent: function(args) {
13285 			var self = this, rng = self.getRng(), tmpElm = self.dom.create("body");
13286 			var se = self.getSel(), whiteSpaceBefore, whiteSpaceAfter, fragment;
13287 
13288 			args = args || {};
13289 			whiteSpaceBefore = whiteSpaceAfter = '';
13290 			args.get = true;
13291 			args.format = args.format || 'html';
13292 			args.selection = true;
13293 			self.editor.fire('BeforeGetContent', args);
13294 
13295 			if (args.format == 'text') {
13296 				return self.isCollapsed() ? '' : (rng.text || (se.toString ? se.toString() : ''));
13297 			}
13298 
13299 			if (rng.cloneContents) {
13300 				fragment = rng.cloneContents();
13301 
13302 				if (fragment) {
13303 					tmpElm.appendChild(fragment);
13304 				}
13305 			} else if (rng.item !== undefined || rng.htmlText !== undefined) {
13306 				// IE will produce invalid markup if elements are present that
13307 				// it doesn't understand like custom elements or HTML5 elements.
13308 				// Adding a BR in front of the contents and then remoiving it seems to fix it though.
13309 				tmpElm.innerHTML = '<br>' + (rng.item ? rng.item(0).outerHTML : rng.htmlText);
13310 				tmpElm.removeChild(tmpElm.firstChild);
13311 			} else {
13312 				tmpElm.innerHTML = rng.toString();
13313 			}
13314 
13315 			// Keep whitespace before and after
13316 			if (/^\s/.test(tmpElm.innerHTML)) {
13317 				whiteSpaceBefore = ' ';
13318 			}
13319 
13320 			if (/\s+$/.test(tmpElm.innerHTML)) {
13321 				whiteSpaceAfter = ' ';
13322 			}
13323 
13324 			args.getInner = true;
13325 
13326 			args.content = self.isCollapsed() ? '' : whiteSpaceBefore + self.serializer.serialize(tmpElm, args) + whiteSpaceAfter;
13327 			self.editor.fire('GetContent', args);
13328 
13329 			return args.content;
13330 		},
13331 
13332 		/**
13333 		 * Sets the current selection to the specified content. If any contents is selected it will be replaced
13334 		 * with the contents passed in to this function. If there is no selection the contents will be inserted
13335 		 * where the caret is placed in the editor/page.
13336 		 *
13337 		 * @method setContent
13338 		 * @param {String} content HTML contents to set could also be other formats depending on settings.
13339 		 * @param {Object} args Optional settings object with for example data format.
13340 		 * @example
13341 		 * // Inserts some HTML contents at the current selection
13342 		 * tinymce.activeEditor.selection.setContent('<strong>Some contents</strong>');
13343 		 */
13344 		setContent: function(content, args) {
13345 			var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;
13346 
13347 			args = args || {format: 'html'};
13348 			args.set = true;
13349 			args.selection = true;
13350 			content = args.content = content;
13351 
13352 			// Dispatch before set content event
13353 			if (!args.no_events) {
13354 				self.editor.fire('BeforeSetContent', args);
13355 			}
13356 
13357 			content = args.content;
13358 
13359 			if (rng.insertNode) {
13360 				// Make caret marker since insertNode places the caret in the beginning of text after insert
13361 				content += '<span id="__caret">_</span>';
13362 
13363 				// Delete and insert new node
13364 				if (rng.startContainer == doc && rng.endContainer == doc) {
13365 					// WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
13366 					doc.body.innerHTML = content;
13367 				} else {
13368 					rng.deleteContents();
13369 
13370 					if (doc.body.childNodes.length === 0) {
13371 						doc.body.innerHTML = content;
13372 					} else {
13373 						// createContextualFragment doesn't exists in IE 9 DOMRanges
13374 						if (rng.createContextualFragment) {
13375 							rng.insertNode(rng.createContextualFragment(content));
13376 						} else {
13377 							// Fake createContextualFragment call in IE 9
13378 							frag = doc.createDocumentFragment();
13379 							temp = doc.createElement('div');
13380 
13381 							frag.appendChild(temp);
13382 							temp.outerHTML = content;
13383 
13384 							rng.insertNode(frag);
13385 						}
13386 					}
13387 				}
13388 
13389 				// Move to caret marker
13390 				caretNode = self.dom.get('__caret');
13391 
13392 				// Make sure we wrap it compleatly, Opera fails with a simple select call
13393 				rng = doc.createRange();
13394 				rng.setStartBefore(caretNode);
13395 				rng.setEndBefore(caretNode);
13396 				self.setRng(rng);
13397 
13398 				// Remove the caret position
13399 				self.dom.remove('__caret');
13400 
13401 				try {
13402 					self.setRng(rng);
13403 				} catch (ex) {
13404 					// Might fail on Opera for some odd reason
13405 				}
13406 			} else {
13407 				if (rng.item) {
13408 					// Delete content and get caret text selection
13409 					doc.execCommand('Delete', false, null);
13410 					rng = self.getRng();
13411 				}
13412 
13413 				// Explorer removes spaces from the beginning of pasted contents
13414 				if (/^\s+/.test(content)) {
13415 					rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);
13416 					self.dom.remove('__mce_tmp');
13417 				} else {
13418 					rng.pasteHTML(content);
13419 				}
13420 			}
13421 
13422 			// Dispatch set content event
13423 			if (!args.no_events) {
13424 				self.editor.fire('SetContent', args);
13425 			}
13426 		},
13427 
13428 		/**
13429 		 * Returns the start element of a selection range. If the start is in a text
13430 		 * node the parent element will be returned.
13431 		 *
13432 		 * @method getStart
13433 		 * @return {Element} Start element of selection range.
13434 		 */
13435 		getStart: function() {
13436 			var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
13437 
13438 			if (rng.duplicate || rng.item) {
13439 				// Control selection, return first item
13440 				if (rng.item) {
13441 					return rng.item(0);
13442 				}
13443 
13444 				// Get start element
13445 				checkRng = rng.duplicate();
13446 				checkRng.collapse(1);
13447 				startElement = checkRng.parentElement();
13448 				if (startElement.ownerDocument !== self.dom.doc) {
13449 					startElement = self.dom.getRoot();
13450 				}
13451 
13452 				// Check if range parent is inside the start element, then return the inner parent element
13453 				// This will fix issues when a single element is selected, IE would otherwise return the wrong start element
13454 				parentElement = node = rng.parentElement();
13455 				while ((node = node.parentNode)) {
13456 					if (node == startElement) {
13457 						startElement = parentElement;
13458 						break;
13459 					}
13460 				}
13461 
13462 				return startElement;
13463 			} else {
13464 				startElement = rng.startContainer;
13465 
13466 				if (startElement.nodeType == 1 && startElement.hasChildNodes()) {
13467 					startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
13468 				}
13469 
13470 				if (startElement && startElement.nodeType == 3) {
13471 					return startElement.parentNode;
13472 				}
13473 
13474 				return startElement;
13475 			}
13476 		},
13477 
13478 		/**
13479 		 * Returns the end element of a selection range. If the end is in a text
13480 		 * node the parent element will be returned.
13481 		 *
13482 		 * @method getEnd
13483 		 * @return {Element} End element of selection range.
13484 		 */
13485 		getEnd: function() {
13486 			var self = this, rng = self.getRng(), endElement, endOffset;
13487 
13488 			if (rng.duplicate || rng.item) {
13489 				if (rng.item) {
13490 					return rng.item(0);
13491 				}
13492 
13493 				rng = rng.duplicate();
13494 				rng.collapse(0);
13495 				endElement = rng.parentElement();
13496 				if (endElement.ownerDocument !== self.dom.doc) {
13497 					endElement = self.dom.getRoot();
13498 				}
13499 
13500 				if (endElement && endElement.nodeName == 'BODY') {
13501 					return endElement.lastChild || endElement;
13502 				}
13503 
13504 				return endElement;
13505 			} else {
13506 				endElement = rng.endContainer;
13507 				endOffset = rng.endOffset;
13508 
13509 				if (endElement.nodeType == 1 && endElement.hasChildNodes()) {
13510 					endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
13511 				}
13512 
13513 				if (endElement && endElement.nodeType == 3) {
13514 					return endElement.parentNode;
13515 				}
13516 
13517 				return endElement;
13518 			}
13519 		},
13520 
13521 		/**
13522 		 * Returns a bookmark location for the current selection. This bookmark object
13523 		 * can then be used to restore the selection after some content modification to the document.
13524 		 *
13525 		 * @method getBookmark
13526 		 * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
13527 		 * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
13528 		 * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
13529 		 * @example
13530 		 * // Stores a bookmark of the current selection
13531 		 * var bm = tinymce.activeEditor.selection.getBookmark();
13532 		 *
13533 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
13534 		 *
13535 		 * // Restore the selection bookmark
13536 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
13537 		 */
13538 		getBookmark: function(type, normalized) {
13539 			var self = this, dom = self.dom, rng, rng2, id, collapsed, name, element, chr = '', styles;
13540 
13541 			function findIndex(name, element) {
13542 				var index = 0;
13543 
13544 				each(dom.select(name), function(node, i) {
13545 					if (node == element) {
13546 						index = i;
13547 					}
13548 				});
13549 
13550 				return index;
13551 			}
13552 
13553 			function normalizeTableCellSelection(rng) {
13554 				function moveEndPoint(start) {
13555 					var container, offset, childNodes, prefix = start ? 'start' : 'end';
13556 
13557 					container = rng[prefix + 'Container'];
13558 					offset = rng[prefix + 'Offset'];
13559 
13560 					if (container.nodeType == 1 && container.nodeName == "TR") {
13561 						childNodes = container.childNodes;
13562 						container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
13563 						if (container) {
13564 							offset = start ? 0 : container.childNodes.length;
13565 							rng['set' + (start ? 'Start' : 'End')](container, offset);
13566 						}
13567 					}
13568 				}
13569 
13570 				moveEndPoint(true);
13571 				moveEndPoint();
13572 
13573 				return rng;
13574 			}
13575 
13576 			function getLocation() {
13577 				var rng = self.getRng(true), root = dom.getRoot(), bookmark = {};
13578 
13579 				function getPoint(rng, start) {
13580 					var container = rng[start ? 'startContainer' : 'endContainer'],
13581 						offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
13582 
13583 					if (container.nodeType == 3) {
13584 						if (normalized) {
13585 							for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
13586 								offset += node.nodeValue.length;
13587 							}
13588 						}
13589 
13590 						point.push(offset);
13591 					} else {
13592 						childNodes = container.childNodes;
13593 
13594 						if (offset >= childNodes.length && childNodes.length) {
13595 							after = 1;
13596 							offset = Math.max(0, childNodes.length - 1);
13597 						}
13598 
13599 						point.push(self.dom.nodeIndex(childNodes[offset], normalized) + after);
13600 					}
13601 
13602 					for (; container && container != root; container = container.parentNode) {
13603 						point.push(self.dom.nodeIndex(container, normalized));
13604 					}
13605 
13606 					return point;
13607 				}
13608 
13609 				bookmark.start = getPoint(rng, true);
13610 
13611 				if (!self.isCollapsed()) {
13612 					bookmark.end = getPoint(rng);
13613 				}
13614 
13615 				return bookmark;
13616 			}
13617 
13618 			if (type == 2) {
13619 				element = self.getNode();
13620 				name = element ? element.nodeName : null;
13621 
13622 				if (name == 'IMG') {
13623 					return {name: name, index: findIndex(name, element)};
13624 				}
13625 
13626 				if (self.tridentSel) {
13627 					return self.tridentSel.getBookmark(type);
13628 				}
13629 
13630 				return getLocation();
13631 			}
13632 
13633 			// Handle simple range
13634 			if (type) {
13635 				return {rng: self.getRng()};
13636 			}
13637 
13638 			rng = self.getRng();
13639 			id = dom.uniqueId();
13640 			collapsed = self.isCollapsed();
13641 			styles = 'overflow:hidden;line-height:0px';
13642 
13643 			// Explorer method
13644 			if (rng.duplicate || rng.item) {
13645 				// Text selection
13646 				if (!rng.item) {
13647 					rng2 = rng.duplicate();
13648 
13649 					try {
13650 						// Insert start marker
13651 						rng.collapse();
13652 						rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
13653 
13654 						// Insert end marker
13655 						if (!collapsed) {
13656 							rng2.collapse(false);
13657 
13658 							// Detect the empty space after block elements in IE and move the
13659 							// end back one character <p></p>] becomes <p>]</p>
13660 							rng.moveToElementText(rng2.parentElement());
13661 							if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
13662 								rng2.move('character', -1);
13663 							}
13664 
13665 							rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
13666 						}
13667 					} catch (ex) {
13668 						// IE might throw unspecified error so lets ignore it
13669 						return null;
13670 					}
13671 				} else {
13672 					// Control selection
13673 					element = rng.item(0);
13674 					name = element.nodeName;
13675 
13676 					return {name: name, index: findIndex(name, element)};
13677 				}
13678 			} else {
13679 				element = self.getNode();
13680 				name = element.nodeName;
13681 				if (name == 'IMG') {
13682 					return {name: name, index: findIndex(name, element)};
13683 				}
13684 
13685 				// W3C method
13686 				rng2 = normalizeTableCellSelection(rng.cloneRange());
13687 
13688 				// Insert end marker
13689 				if (!collapsed) {
13690 					rng2.collapse(false);
13691 					rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
13692 				}
13693 
13694 				rng = normalizeTableCellSelection(rng);
13695 				rng.collapse(true);
13696 				rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
13697 			}
13698 
13699 			self.moveToBookmark({id: id, keep: 1});
13700 
13701 			return {id: id};
13702 		},
13703 
13704 		/**
13705 		 * Restores the selection to the specified bookmark.
13706 		 *
13707 		 * @method moveToBookmark
13708 		 * @param {Object} bookmark Bookmark to restore selection from.
13709 		 * @return {Boolean} true/false if it was successful or not.
13710 		 * @example
13711 		 * // Stores a bookmark of the current selection
13712 		 * var bm = tinymce.activeEditor.selection.getBookmark();
13713 		 *
13714 		 * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
13715 		 *
13716 		 * // Restore the selection bookmark
13717 		 * tinymce.activeEditor.selection.moveToBookmark(bm);
13718 		 */
13719 		moveToBookmark: function(bookmark) {
13720 			var self = this, dom = self.dom, rng, root, startContainer, endContainer, startOffset, endOffset;
13721 
13722 			function setEndPoint(start) {
13723 				var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
13724 
13725 				if (point) {
13726 					offset = point[0];
13727 
13728 					// Find container node
13729 					for (node = root, i = point.length - 1; i >= 1; i--) {
13730 						children = node.childNodes;
13731 
13732 						if (point[i] > children.length - 1) {
13733 							return;
13734 						}
13735 
13736 						node = children[point[i]];
13737 					}
13738 
13739 					// Move text offset to best suitable location
13740 					if (node.nodeType === 3) {
13741 						offset = Math.min(point[0], node.nodeValue.length);
13742 					}
13743 
13744 					// Move element offset to best suitable location
13745 					if (node.nodeType === 1) {
13746 						offset = Math.min(point[0], node.childNodes.length);
13747 					}
13748 
13749 					// Set offset within container node
13750 					if (start) {
13751 						rng.setStart(node, offset);
13752 					} else {
13753 						rng.setEnd(node, offset);
13754 					}
13755 				}
13756 
13757 				return true;
13758 			}
13759 
13760 			function restoreEndPoint(suffix) {
13761 				var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
13762 
13763 				if (marker) {
13764 					node = marker.parentNode;
13765 
13766 					if (suffix == 'start') {
13767 						if (!keep) {
13768 							idx = dom.nodeIndex(marker);
13769 						} else {
13770 							node = marker.firstChild;
13771 							idx = 1;
13772 						}
13773 
13774 						startContainer = endContainer = node;
13775 						startOffset = endOffset = idx;
13776 					} else {
13777 						if (!keep) {
13778 							idx = dom.nodeIndex(marker);
13779 						} else {
13780 							node = marker.firstChild;
13781 							idx = 1;
13782 						}
13783 
13784 						endContainer = node;
13785 						endOffset = idx;
13786 					}
13787 
13788 					if (!keep) {
13789 						prev = marker.previousSibling;
13790 						next = marker.nextSibling;
13791 
13792 						// Remove all marker text nodes
13793 						each(grep(marker.childNodes), function(node) {
13794 							if (node.nodeType == 3) {
13795 								node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
13796 							}
13797 						});
13798 
13799 						// Remove marker but keep children if for example contents where inserted into the marker
13800 						// Also remove duplicated instances of the marker for example by a
13801 						// split operation or by WebKit auto split on paste feature
13802 						while ((marker = dom.get(bookmark.id + '_' + suffix))) {
13803 							dom.remove(marker, 1);
13804 						}
13805 
13806 						// If siblings are text nodes then merge them unless it's Opera since it some how removes the node
13807 						// and we are sniffing since adding a lot of detection code for a browser with 3% of the market
13808 						// isn't worth the effort. Sorry, Opera but it's just a fact
13809 						if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !isOpera) {
13810 							idx = prev.nodeValue.length;
13811 							prev.appendData(next.nodeValue);
13812 							dom.remove(next);
13813 
13814 							if (suffix == 'start') {
13815 								startContainer = endContainer = prev;
13816 								startOffset = endOffset = idx;
13817 							} else {
13818 								endContainer = prev;
13819 								endOffset = idx;
13820 							}
13821 						}
13822 					}
13823 				}
13824 			}
13825 
13826 			function addBogus(node) {
13827 				// Adds a bogus BR element for empty block elements
13828 				if (dom.isBlock(node) && !node.innerHTML && !isIE) {
13829 					node.innerHTML = '<br data-mce-bogus="1" />';
13830 				}
13831 
13832 				return node;
13833 			}
13834 
13835 			if (bookmark) {
13836 				if (bookmark.start) {
13837 					rng = dom.createRng();
13838 					root = dom.getRoot();
13839 
13840 					if (self.tridentSel) {
13841 						return self.tridentSel.moveToBookmark(bookmark);
13842 					}
13843 
13844 					if (setEndPoint(true) && setEndPoint()) {
13845 						self.setRng(rng);
13846 					}
13847 				} else if (bookmark.id) {
13848 					// Restore start/end points
13849 					restoreEndPoint('start');
13850 					restoreEndPoint('end');
13851 
13852 					if (startContainer) {
13853 						rng = dom.createRng();
13854 						rng.setStart(addBogus(startContainer), startOffset);
13855 						rng.setEnd(addBogus(endContainer), endOffset);
13856 						self.setRng(rng);
13857 					}
13858 				} else if (bookmark.name) {
13859 					self.select(dom.select(bookmark.name)[bookmark.index]);
13860 				} else if (bookmark.rng) {
13861 					self.setRng(bookmark.rng);
13862 				}
13863 			}
13864 		},
13865 
13866 		/**
13867 		 * Selects the specified element. This will place the start and end of the selection range around the element.
13868 		 *
13869 		 * @method select
13870 		 * @param {Element} node HMTL DOM element to select.
13871 		 * @param {Boolean} content Optional bool state if the contents should be selected or not on non IE browser.
13872 		 * @return {Element} Selected element the same element as the one that got passed in.
13873 		 * @example
13874 		 * // Select the first paragraph in the active editor
13875 		 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
13876 		 */
13877 		select: function(node, content) {
13878 			var self = this, dom = self.dom, rng = dom.createRng(), idx;
13879 
13880 			// Clear stored range set by FocusManager
13881 			self.lastFocusBookmark = null;
13882 
13883 			if (node) {
13884 				if (!content && self.controlSelection.controlSelect(node)) {
13885 					return;
13886 				}
13887 
13888 				idx = dom.nodeIndex(node);
13889 				rng.setStart(node.parentNode, idx);
13890 				rng.setEnd(node.parentNode, idx + 1);
13891 
13892 				// Find first/last text node or BR element
13893 				if (content) {
13894 					self._moveEndPoint(rng, node, true);
13895 					self._moveEndPoint(rng, node);
13896 				}
13897 
13898 				self.setRng(rng);
13899 			}
13900 
13901 			return node;
13902 		},
13903 
13904 		/**
13905 		 * Returns true/false if the selection range is collapsed or not. Collapsed means if it's a caret or a larger selection.
13906 		 *
13907 		 * @method isCollapsed
13908 		 * @return {Boolean} true/false state if the selection range is collapsed or not.
13909 		 * Collapsed means if it's a caret or a larger selection.
13910 		 */
13911 		isCollapsed: function() {
13912 			var self = this, rng = self.getRng(), sel = self.getSel();
13913 
13914 			if (!rng || rng.item) {
13915 				return false;
13916 			}
13917 
13918 			if (rng.compareEndPoints) {
13919 				return rng.compareEndPoints('StartToEnd', rng) === 0;
13920 			}
13921 
13922 			return !sel || rng.collapsed;
13923 		},
13924 
13925 		/**
13926 		 * Collapse the selection to start or end of range.
13927 		 *
13928 		 * @method collapse
13929 		 * @param {Boolean} to_start Optional boolean state if to collapse to end or not. Defaults to start.
13930 		 */
13931 		collapse: function(to_start) {
13932 			var self = this, rng = self.getRng(), node;
13933 
13934 			// Control range on IE
13935 			if (rng.item) {
13936 				node = rng.item(0);
13937 				rng = self.win.document.body.createTextRange();
13938 				rng.moveToElementText(node);
13939 			}
13940 
13941 			rng.collapse(!!to_start);
13942 			self.setRng(rng);
13943 		},
13944 
13945 		/**
13946 		 * Returns the browsers internal selection object.
13947 		 *
13948 		 * @method getSel
13949 		 * @return {Selection} Internal browser selection object.
13950 		 */
13951 		getSel: function() {
13952 			var win = this.win;
13953 
13954 			return win.getSelection ? win.getSelection() : win.document.selection;
13955 		},
13956 
13957 		/**
13958 		 * Returns the browsers internal range object.
13959 		 *
13960 		 * @method getRng
13961 		 * @param {Boolean} w3c Forces a compatible W3C range on IE.
13962 		 * @return {Range} Internal browser range object.
13963 		 * @see http://www.quirksmode.org/dom/range_intro.html
13964 		 * @see http://www.dotvoid.com/2001/03/using-the-range-object-in-mozilla/
13965 		 */
13966 		getRng: function(w3c) {
13967 			var self = this, selection, rng, elm, doc = self.win.document, ieRng;
13968 
13969 			function tryCompareBounderyPoints(how, sourceRange, destinationRange) {
13970 				try {
13971 					return sourceRange.compareBoundaryPoints(how, destinationRange);
13972 				} catch (ex) {
13973 					// Gecko throws wrong document exception if the range points
13974 					// to nodes that where removed from the dom #6690
13975 					// Browsers should mutate existing DOMRange instances so that they always point
13976 					// to something in the document this is not the case in Gecko works fine in IE/WebKit/Blink
13977 					// For performance reasons just return -1
13978 					return -1;
13979 				}
13980 			}
13981 
13982 			// Use last rng passed from FocusManager if it's available this enables
13983 			// calls to editor.selection.getStart() to work when caret focus is lost on IE
13984 			if (!w3c && self.lastFocusBookmark) {
13985 				var bookmark = self.lastFocusBookmark;
13986 
13987 				// Convert bookmark to range IE 11 fix
13988 				if (bookmark.startContainer) {
13989 					rng = doc.createRange();
13990 					rng.setStart(bookmark.startContainer, bookmark.startOffset);
13991 					rng.setEnd(bookmark.endContainer, bookmark.endOffset);
13992 				} else {
13993 					rng = bookmark;
13994 				}
13995 
13996 				return rng;
13997 			}
13998 
13999 			// Found tridentSel object then we need to use that one
14000 			if (w3c && self.tridentSel) {
14001 				return self.tridentSel.getRangeAt(0);
14002 			}
14003 
14004 			try {
14005 				if ((selection = self.getSel())) {
14006 					if (selection.rangeCount > 0) {
14007 						rng = selection.getRangeAt(0);
14008 					} else {
14009 						rng = selection.createRange ? selection.createRange() : doc.createRange();
14010 					}
14011 				}
14012 			} catch (ex) {
14013 				// IE throws unspecified error here if TinyMCE is placed in a frame/iframe
14014 			}
14015 
14016 			// We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet
14017 			// IE 11 doesn't support the selection object so we check for that as well
14018 			if (isIE && rng && rng.setStart && doc.selection) {
14019 				try {
14020 					// IE will sometimes throw an exception here
14021 					ieRng = doc.selection.createRange();
14022 				} catch (ex) {
14023 
14024 				}
14025 
14026 				if (ieRng && ieRng.item) {
14027 					elm = ieRng.item(0);
14028 					rng = doc.createRange();
14029 					rng.setStartBefore(elm);
14030 					rng.setEndAfter(elm);
14031 				}
14032 			}
14033 
14034 			// No range found then create an empty one
14035 			// This can occur when the editor is placed in a hidden container element on Gecko
14036 			// Or on IE when there was an exception
14037 			if (!rng) {
14038 				rng = doc.createRange ? doc.createRange() : doc.body.createTextRange();
14039 			}
14040 
14041 			// If range is at start of document then move it to start of body
14042 			if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) {
14043 				elm = self.dom.getRoot();
14044 				rng.setStart(elm, 0);
14045 				rng.setEnd(elm, 0);
14046 			}
14047 
14048 			if (self.selectedRange && self.explicitRange) {
14049 				if (tryCompareBounderyPoints(rng.START_TO_START, rng, self.selectedRange) === 0 &&
14050 					tryCompareBounderyPoints(rng.END_TO_END, rng, self.selectedRange) === 0) {
14051 					// Safari, Opera and Chrome only ever select text which causes the range to change.
14052 					// This lets us use the originally set range if the selection hasn't been changed by the user.
14053 					rng = self.explicitRange;
14054 				} else {
14055 					self.selectedRange = null;
14056 					self.explicitRange = null;
14057 				}
14058 			}
14059 
14060 			return rng;
14061 		},
14062 
14063 		/**
14064 		 * Changes the selection to the specified DOM range.
14065 		 *
14066 		 * @method setRng
14067 		 * @param {Range} rng Range to select.
14068 		 */
14069 		setRng: function(rng, forward) {
14070 			var self = this, sel;
14071 
14072 			// Is IE specific range
14073 			if (rng.select) {
14074 				try {
14075 					rng.select();
14076 				} catch (ex) {
14077 					// Needed for some odd IE bug #1843306
14078 				}
14079 
14080 				return;
14081 			}
14082 
14083 			if (!self.tridentSel) {
14084 				sel = self.getSel();
14085 
14086 				if (sel) {
14087 					self.explicitRange = rng;
14088 
14089 					try {
14090 						sel.removeAllRanges();
14091 						sel.addRange(rng);
14092 					} catch (ex) {
14093 						// IE might throw errors here if the editor is within a hidden container and selection is changed
14094 					}
14095 
14096 					// Forward is set to false and we have an extend function
14097 					if (forward === false && sel.extend) {
14098 						sel.collapse(rng.endContainer, rng.endOffset);
14099 						sel.extend(rng.startContainer, rng.startOffset);
14100 					}
14101 
14102 					// adding range isn't always successful so we need to check range count otherwise an exception can occur
14103 					self.selectedRange = sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
14104 				}
14105 			} else {
14106 				// Is W3C Range fake range on IE
14107 				if (rng.cloneRange) {
14108 					try {
14109 						self.tridentSel.addRange(rng);
14110 						return;
14111 					} catch (ex) {
14112 						//IE9 throws an error here if called before selection is placed in the editor
14113 					}
14114 				}
14115 			}
14116 		},
14117 
14118 		/**
14119 		 * Sets the current selection to the specified DOM element.
14120 		 *
14121 		 * @method setNode
14122 		 * @param {Element} elm Element to set as the contents of the selection.
14123 		 * @return {Element} Returns the element that got passed in.
14124 		 * @example
14125 		 * // Inserts a DOM node at current selection/caret location
14126 		 * tinymce.activeEditor.selection.setNode(tinymce.activeEditor.dom.create('img', {src: 'some.gif', title: 'some title'}));
14127 		 */
14128 		setNode: function(elm) {
14129 			var self = this;
14130 
14131 			self.setContent(self.dom.getOuterHTML(elm));
14132 
14133 			return elm;
14134 		},
14135 
14136 		/**
14137 		 * Returns the currently selected element or the common ancestor element for both start and end of the selection.
14138 		 *
14139 		 * @method getNode
14140 		 * @return {Element} Currently selected element or common ancestor element.
14141 		 * @example
14142 		 * // Alerts the currently selected elements node name
14143 		 * alert(tinymce.activeEditor.selection.getNode().nodeName);
14144 		 */
14145 		getNode: function() {
14146 			var self = this, rng = self.getRng(), elm;
14147 			var startContainer = rng.startContainer, endContainer = rng.endContainer;
14148 			var startOffset = rng.startOffset, endOffset = rng.endOffset, root = self.dom.getRoot();
14149 
14150 			function skipEmptyTextNodes(node, forwards) {
14151 				var orig = node;
14152 
14153 				while (node && node.nodeType === 3 && node.length === 0) {
14154 					node = forwards ? node.nextSibling : node.previousSibling;
14155 				}
14156 
14157 				return node || orig;
14158 			}
14159 
14160 			// Range maybe lost after the editor is made visible again
14161 			if (!rng) {
14162 				return root;
14163 			}
14164 
14165 			if (rng.setStart) {
14166 				elm = rng.commonAncestorContainer;
14167 
14168 				// Handle selection a image or other control like element such as anchors
14169 				if (!rng.collapsed) {
14170 					if (startContainer == endContainer) {
14171 						if (endOffset - startOffset < 2) {
14172 							if (startContainer.hasChildNodes()) {
14173 								elm = startContainer.childNodes[startOffset];
14174 							}
14175 						}
14176 					}
14177 
14178 					// If the anchor node is a element instead of a text node then return this element
14179 					//if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)
14180 					//	return sel.anchorNode.childNodes[sel.anchorOffset];
14181 
14182 					// Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.
14183 					// This happens when you double click an underlined word in FireFox.
14184 					if (startContainer.nodeType === 3 && endContainer.nodeType === 3) {
14185 						if (startContainer.length === startOffset) {
14186 							startContainer = skipEmptyTextNodes(startContainer.nextSibling, true);
14187 						} else {
14188 							startContainer = startContainer.parentNode;
14189 						}
14190 
14191 						if (endOffset === 0) {
14192 							endContainer = skipEmptyTextNodes(endContainer.previousSibling, false);
14193 						} else {
14194 							endContainer = endContainer.parentNode;
14195 						}
14196 
14197 						if (startContainer && startContainer === endContainer) {
14198 							return startContainer;
14199 						}
14200 					}
14201 				}
14202 
14203 				if (elm && elm.nodeType == 3) {
14204 					return elm.parentNode;
14205 				}
14206 
14207 				return elm;
14208 			}
14209 
14210 			elm = rng.item ? rng.item(0) : rng.parentElement();
14211 
14212 			// IE 7 might return elements outside the iframe
14213 			if (elm.ownerDocument !== self.win.document) {
14214 				elm = root;
14215 			}
14216 
14217 			return elm;
14218 		},
14219 
14220 		getSelectedBlocks: function(startElm, endElm) {
14221 			var self = this, dom = self.dom, node, root, selectedBlocks = [];
14222 
14223 			root = dom.getRoot();
14224 			startElm = dom.getParent(startElm || self.getStart(), dom.isBlock);
14225 			endElm = dom.getParent(endElm || self.getEnd(), dom.isBlock);
14226 
14227 			if (startElm && startElm != root) {
14228 				selectedBlocks.push(startElm);
14229 			}
14230 
14231 			if (startElm && endElm && startElm != endElm) {
14232 				node = startElm;
14233 
14234 				var walker = new TreeWalker(startElm, root);
14235 				while ((node = walker.next()) && node != endElm) {
14236 					if (dom.isBlock(node)) {
14237 						selectedBlocks.push(node);
14238 					}
14239 				}
14240 			}
14241 
14242 			if (endElm && startElm != endElm && endElm != root) {
14243 				selectedBlocks.push(endElm);
14244 			}
14245 
14246 			return selectedBlocks;
14247 		},
14248 
14249 		isForward: function() {
14250 			var dom = this.dom, sel = this.getSel(), anchorRange, focusRange;
14251 
14252 			// No support for selection direction then always return true
14253 			if (!sel || !sel.anchorNode || !sel.focusNode) {
14254 				return true;
14255 			}
14256 
14257 			anchorRange = dom.createRng();
14258 			anchorRange.setStart(sel.anchorNode, sel.anchorOffset);
14259 			anchorRange.collapse(true);
14260 
14261 			focusRange = dom.createRng();
14262 			focusRange.setStart(sel.focusNode, sel.focusOffset);
14263 			focusRange.collapse(true);
14264 
14265 			return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
14266 		},
14267 
14268 		normalize: function() {
14269 			var self = this, rng = self.getRng();
14270 
14271 			if (!isIE && new RangeUtils(self.dom).normalize(rng)) {
14272 				self.setRng(rng, self.isForward());
14273 			}
14274 
14275 			return rng;
14276 		},
14277 
14278 		/**
14279 		 * Executes callback of the current selection matches the specified selector or not and passes the state and args to the callback.
14280 		 *
14281 		 * @method selectorChanged
14282 		 * @param {String} selector CSS selector to check for.
14283 		 * @param {function} callback Callback with state and args when the selector is matches or not.
14284 		 */
14285 		selectorChanged: function(selector, callback) {
14286 			var self = this, currentSelectors;
14287 
14288 			if (!self.selectorChangedData) {
14289 				self.selectorChangedData = {};
14290 				currentSelectors = {};
14291 
14292 				self.editor.on('NodeChange', function(e) {
14293 					var node = e.element, dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};
14294 
14295 					// Check for new matching selectors
14296 					each(self.selectorChangedData, function(callbacks, selector) {
14297 						each(parents, function(node) {
14298 							if (dom.is(node, selector)) {
14299 								if (!currentSelectors[selector]) {
14300 									// Execute callbacks
14301 									each(callbacks, function(callback) {
14302 										callback(true, {node: node, selector: selector, parents: parents});
14303 									});
14304 
14305 									currentSelectors[selector] = callbacks;
14306 								}
14307 
14308 								matchedSelectors[selector] = callbacks;
14309 								return false;
14310 							}
14311 						});
14312 					});
14313 
14314 					// Check if current selectors still match
14315 					each(currentSelectors, function(callbacks, selector) {
14316 						if (!matchedSelectors[selector]) {
14317 							delete currentSelectors[selector];
14318 
14319 							each(callbacks, function(callback) {
14320 								callback(false, {node: node, selector: selector, parents: parents});
14321 							});
14322 						}
14323 					});
14324 				});
14325 			}
14326 
14327 			// Add selector listeners
14328 			if (!self.selectorChangedData[selector]) {
14329 				self.selectorChangedData[selector] = [];
14330 			}
14331 
14332 			self.selectorChangedData[selector].push(callback);
14333 
14334 			return self;
14335 		},
14336 
14337 		getScrollContainer: function() {
14338 			var scrollContainer, node = this.dom.getRoot();
14339 
14340 			while (node && node.nodeName != 'BODY') {
14341 				if (node.scrollHeight > node.clientHeight) {
14342 					scrollContainer = node;
14343 					break;
14344 				}
14345 
14346 				node = node.parentNode;
14347 			}
14348 
14349 			return scrollContainer;
14350 		},
14351 
14352 		scrollIntoView: function(elm) {
14353 			var y, viewPort, self = this, dom = self.dom, root = dom.getRoot(), viewPortY, viewPortH;
14354 
14355 			function getPos(elm) {
14356 				var x = 0, y = 0;
14357 
14358 				var offsetParent = elm;
14359 				while (offsetParent && offsetParent.nodeType) {
14360 					x += offsetParent.offsetLeft || 0;
14361 					y += offsetParent.offsetTop || 0;
14362 					offsetParent = offsetParent.offsetParent;
14363 				}
14364 
14365 				return {x: x, y: y};
14366 			}
14367 
14368 			if (root.nodeName != 'BODY') {
14369 				var scrollContainer = self.getScrollContainer();
14370 				if (scrollContainer) {
14371 					y = getPos(elm).y - getPos(scrollContainer).y;
14372 					viewPortH = scrollContainer.clientHeight;
14373 					viewPortY = scrollContainer.scrollTop;
14374 					if (y < viewPortY || y + 25 > viewPortY + viewPortH) {
14375 						scrollContainer.scrollTop = y < viewPortY ? y : y - viewPortH + 25;
14376 					}
14377 
14378 					return;
14379 				}
14380 			}
14381 
14382 			viewPort = dom.getViewPort(self.editor.getWin());
14383 			y = dom.getPos(elm).y;
14384 			viewPortY = viewPort.y;
14385 			viewPortH = viewPort.h;
14386 			if (y < viewPort.y || y + 25 > viewPortY + viewPortH) {
14387 				self.editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25);
14388 			}
14389 		},
14390 
14391 		_moveEndPoint: function(rng, node, start) {
14392 			var root = node, walker = new TreeWalker(node, root);
14393 			var nonEmptyElementsMap = this.dom.schema.getNonEmptyElements();
14394 
14395 			do {
14396 				// Text node
14397 				if (node.nodeType == 3 && trim(node.nodeValue).length !== 0) {
14398 					if (start) {
14399 						rng.setStart(node, 0);
14400 					} else {
14401 						rng.setEnd(node, node.nodeValue.length);
14402 					}
14403 
14404 					return;
14405 				}
14406 
14407 				// BR/IMG/INPUT elements
14408 				if (nonEmptyElementsMap[node.nodeName]) {
14409 					if (start) {
14410 						rng.setStartBefore(node);
14411 					} else {
14412 						if (node.nodeName == 'BR') {
14413 							rng.setEndBefore(node);
14414 						} else {
14415 							rng.setEndAfter(node);
14416 						}
14417 					}
14418 
14419 					return;
14420 				}
14421 
14422 				// Found empty text block old IE can place the selection inside those
14423 				if (Env.ie && Env.ie < 11 && this.dom.isBlock(node) && this.dom.isEmpty(node)) {
14424 					if (start) {
14425 						rng.setStart(node, 0);
14426 					} else {
14427 						rng.setEnd(node, 0);
14428 					}
14429 
14430 					return;
14431 				}
14432 			} while ((node = (start ? walker.next() : walker.prev())));
14433 
14434 			// Failed to find any text node or other suitable location then move to the root of body
14435 			if (root.nodeName == 'BODY') {
14436 				if (start) {
14437 					rng.setStart(root, 0);
14438 				} else {
14439 					rng.setEnd(root, root.childNodes.length);
14440 				}
14441 			}
14442 		},
14443 
14444 		destroy: function() {
14445 			this.win = null;
14446 			this.controlSelection.destroy();
14447 		}
14448 	};
14449 
14450 	return Selection;
14451 });
14452 
14453 // Included from: js/tinymce/classes/fmt/Preview.js
14454 
14455 /**
14456  * Preview.js
14457  *
14458  * Copyright, Moxiecode Systems AB
14459  * Released under LGPL License.
14460  *
14461  * License: http://www.tinymce.com/license
14462  * Contributing: http://www.tinymce.com/contributing
14463  */
14464 
14465 /**
14466  * Internal class for generating previews styles for formats.
14467  *
14468  * Example:
14469  *  Preview.getCssText(editor, 'bold');
14470  *
14471  * @class tinymce.fmt.Preview
14472  * @private
14473  */
14474 define("tinymce/fmt/Preview", [
14475 	"tinymce/util/Tools"
14476 ], function(Tools) {
14477 	var each = Tools.each;
14478 
14479 	function getCssText(editor, format) {
14480 		var name, previewElm, dom = editor.dom;
14481 		var previewCss = '', parentFontSize, previewStyles;
14482 
14483 		previewStyles = editor.settings.preview_styles;
14484 
14485 		// No preview forced
14486 		if (previewStyles === false) {
14487 			return '';
14488 		}
14489 
14490 		// Default preview
14491 		if (!previewStyles) {
14492 			previewStyles = 'font-family font-size font-weight font-style text-decoration ' +
14493 				'text-transform color background-color border border-radius outline text-shadow';
14494 		}
14495 
14496 		// Removes any variables since these can't be previewed
14497 		function removeVars(val) {
14498 			return val.replace(/%(\w+)/g, '');
14499 		}
14500 
14501 		// Create block/inline element to use for preview
14502 		if (typeof(format) == "string") {
14503 			format = editor.formatter.get(format);
14504 			if (!format) {
14505 				return;
14506 			}
14507 
14508 			format = format[0];
14509 		}
14510 
14511 		name = format.block || format.inline || 'span';
14512 		previewElm = dom.create(name);
14513 
14514 		// Add format styles to preview element
14515 		each(format.styles, function(value, name) {
14516 			value = removeVars(value);
14517 
14518 			if (value) {
14519 				dom.setStyle(previewElm, name, value);
14520 			}
14521 		});
14522 
14523 		// Add attributes to preview element
14524 		each(format.attributes, function(value, name) {
14525 			value = removeVars(value);
14526 
14527 			if (value) {
14528 				dom.setAttrib(previewElm, name, value);
14529 			}
14530 		});
14531 
14532 		// Add classes to preview element
14533 		each(format.classes, function(value) {
14534 			value = removeVars(value);
14535 
14536 			if (!dom.hasClass(previewElm, value)) {
14537 				dom.addClass(previewElm, value);
14538 			}
14539 		});
14540 
14541 		editor.fire('PreviewFormats');
14542 
14543 		// Add the previewElm outside the visual area
14544 		dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
14545 		editor.getBody().appendChild(previewElm);
14546 
14547 		// Get parent container font size so we can compute px values out of em/% for older IE:s
14548 		parentFontSize = dom.getStyle(editor.getBody(), 'fontSize', true);
14549 		parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0;
14550 
14551 		each(previewStyles.split(' '), function(name) {
14552 			var value = dom.getStyle(previewElm, name, true);
14553 
14554 			// If background is transparent then check if the body has a background color we can use
14555 			if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
14556 				value = dom.getStyle(editor.getBody(), name, true);
14557 
14558 				// Ignore white since it's the default color, not the nicest fix
14559 				// TODO: Fix this by detecting runtime style
14560 				if (dom.toHex(value).toLowerCase() == '#ffffff') {
14561 					return;
14562 				}
14563 			}
14564 
14565 			if (name == 'color') {
14566 				// Ignore black since it's the default color, not the nicest fix
14567 				// TODO: Fix this by detecting runtime style
14568 				if (dom.toHex(value).toLowerCase() == '#000000') {
14569 					return;
14570 				}
14571 			}
14572 
14573 			// Old IE won't calculate the font size so we need to do that manually
14574 			if (name == 'font-size') {
14575 				if (/em|%$/.test(value)) {
14576 					if (parentFontSize === 0) {
14577 						return;
14578 					}
14579 
14580 					// Convert font size from em/% to px
14581 					value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1);
14582 					value = (value * parentFontSize) + 'px';
14583 				}
14584 			}
14585 
14586 			if (name == "border" && value) {
14587 				previewCss += 'padding:0 2px;';
14588 			}
14589 
14590 			previewCss += name + ':' + value + ';';
14591 		});
14592 
14593 		editor.fire('AfterPreviewFormats');
14594 
14595 		//previewCss += 'line-height:normal';
14596 
14597 		dom.remove(previewElm);
14598 
14599 		return previewCss;
14600 	}
14601 
14602 	return {
14603 		getCssText: getCssText
14604 	};
14605 });
14606 
14607 // Included from: js/tinymce/classes/Formatter.js
14608 
14609 /**
14610  * Formatter.js
14611  *
14612  * Copyright, Moxiecode Systems AB
14613  * Released under LGPL License.
14614  *
14615  * License: http://www.tinymce.com/license
14616  * Contributing: http://www.tinymce.com/contributing
14617  */
14618 
14619 /**
14620  * Text formatter engine class. This class is used to apply formats like bold, italic, font size
14621  * etc to the current selection or specific nodes. This engine was build to replace the browsers
14622  * default formatting logic for execCommand due to it's inconsistent and buggy behavior.
14623  *
14624  * @class tinymce.Formatter
14625  * @example
14626  *  tinymce.activeEditor.formatter.register('mycustomformat', {
14627  *    inline: 'span',
14628  *    styles: {color: '#ff0000'}
14629  *  });
14630  *
14631  *  tinymce.activeEditor.formatter.apply('mycustomformat');
14632  */
14633 define("tinymce/Formatter", [
14634 	"tinymce/dom/TreeWalker",
14635 	"tinymce/dom/RangeUtils",
14636 	"tinymce/util/Tools",
14637 	"tinymce/fmt/Preview"
14638 ], function(TreeWalker, RangeUtils, Tools, Preview) {
14639 	/**
14640 	 * Constructs a new formatter instance.
14641 	 *
14642 	 * @constructor Formatter
14643 	 * @param {tinymce.Editor} ed Editor instance to construct the formatter engine to.
14644 	 */
14645 	return function(ed) {
14646 		var formats = {},
14647 			dom = ed.dom,
14648 			selection = ed.selection,
14649 			rangeUtils = new RangeUtils(dom),
14650 			isValid = ed.schema.isValidChild,
14651 			isBlock = dom.isBlock,
14652 			forcedRootBlock = ed.settings.forced_root_block,
14653 			nodeIndex = dom.nodeIndex,
14654 			INVISIBLE_CHAR = '\uFEFF',
14655 			MCE_ATTR_RE = /^(src|href|style)$/,
14656 			FALSE = false,
14657 			TRUE = true,
14658 			formatChangeData,
14659 			undef,
14660 			getContentEditable = dom.getContentEditable,
14661 			disableCaretContainer,
14662 			markCaretContainersBogus;
14663 
14664 		var each = Tools.each,
14665 			grep = Tools.grep,
14666 			walk = Tools.walk,
14667 			extend = Tools.extend;
14668 
14669 		function isTextBlock(name) {
14670 			if (name.nodeType) {
14671 				name = name.nodeName;
14672 			}
14673 
14674 			return !!ed.schema.getTextBlockElements()[name.toLowerCase()];
14675 		}
14676 
14677 		function getParents(node, selector) {
14678 			return dom.getParents(node, selector, dom.getRoot());
14679 		}
14680 
14681 		function isCaretNode(node) {
14682 			return node.nodeType === 1 && node.id === '_mce_caret';
14683 		}
14684 
14685 		function defaultFormats() {
14686 			register({
14687 				
14688 				valigntop: [
14689 					{selector: 'td,th', styles: {'verticalAlign': 'top'}}
14690 				],
14691 
14692 				valignmiddle: [
14693 					{selector: 'td,th', styles: {'verticalAlign': 'middle'}}
14694 				],
14695 
14696 				valignbottom: [
14697 					{selector: 'td,th', styles: {'verticalAlign': 'bottom'}}
14698 				],
14699 
14700 				alignleft: [
14701 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'left'}, defaultBlock: 'div'},
14702 					{selector: 'img,table', collapsed: false, styles: {'float': 'left'}}
14703 				],
14704 
14705 				aligncenter: [
14706 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'center'}, defaultBlock: 'div'},
14707 					{selector: 'img', collapsed: false, styles: {display: 'block', marginLeft: 'auto', marginRight: 'auto'}},
14708 					{selector: 'table', collapsed: false, styles: {marginLeft: 'auto', marginRight: 'auto'}}
14709 				],
14710 
14711 				alignright: [
14712 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'right'}, defaultBlock: 'div'},
14713 					{selector: 'img,table', collapsed: false, styles: {'float': 'right'}}
14714 				],
14715 
14716 				alignjustify: [
14717 					{selector: 'figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li', styles: {textAlign: 'justify'}, defaultBlock: 'div'}
14718 				],
14719 
14720 				bold: [
14721 					{inline: 'strong', remove: 'all'},
14722 					{inline: 'span', styles: {fontWeight: 'bold'}},
14723 					{inline: 'b', remove: 'all'}
14724 				],
14725 
14726 				italic: [
14727 					{inline: 'em', remove: 'all'},
14728 					{inline: 'span', styles: {fontStyle: 'italic'}},
14729 					{inline: 'i', remove: 'all'}
14730 				],
14731 
14732 				underline: [
14733 					{inline: 'span', styles: {textDecoration: 'underline'}, exact: true},
14734 					{inline: 'u', remove: 'all'}
14735 				],
14736 
14737 				strikethrough: [
14738 					{inline: 'span', styles: {textDecoration: 'line-through'}, exact: true},
14739 					{inline: 'strike', remove: 'all'}
14740 				],
14741 
14742 				forecolor: {inline: 'span', styles: {color: '%value'}, wrap_links: false},
14743 				hilitecolor: {inline: 'span', styles: {backgroundColor: '%value'}, wrap_links: false},
14744 				fontname: {inline: 'span', styles: {fontFamily: '%value'}},
14745 				fontsize: {inline: 'span', styles: {fontSize: '%value'}},
14746 				fontsize_class: {inline: 'span', attributes: {'class': '%value'}},
14747 				blockquote: {block: 'blockquote', wrapper: 1, remove: 'all'},
14748 				subscript: {inline: 'sub'},
14749 				superscript: {inline: 'sup'},
14750 				code: {inline: 'code'},
14751 
14752 				link: {inline: 'a', selector: 'a', remove: 'all', split: true, deep: true,
14753 					onmatch: function() {
14754 						return true;
14755 					},
14756 
14757 					onformat: function(elm, fmt, vars) {
14758 						each(vars, function(value, key) {
14759 							dom.setAttrib(elm, key, value);
14760 						});
14761 					}
14762 				},
14763 
14764 				removeformat: [
14765 					{
14766 						selector: 'b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q',
14767 						remove: 'all',
14768 						split: true,
14769 						expand: false,
14770 						block_expand: true,
14771 						deep: true
14772 					},
14773 					{selector: 'span', attributes: ['style', 'class'], remove: 'empty', split: true, expand: false, deep: true},
14774 					{selector: '*', attributes: ['style', 'class'], split: false, expand: false, deep: true}
14775 				]
14776 			});
14777 
14778 			// Register default block formats
14779 			each('p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp'.split(/\s/), function(name) {
14780 				register(name, {block: name, remove: 'all'});
14781 			});
14782 
14783 			// Register user defined formats
14784 			register(ed.settings.formats);
14785 		}
14786 
14787 		function addKeyboardShortcuts() {
14788 			// Add some inline shortcuts
14789 			ed.addShortcut('ctrl+b', 'bold_desc', 'Bold');
14790 			ed.addShortcut('ctrl+i', 'italic_desc', 'Italic');
14791 			ed.addShortcut('ctrl+u', 'underline_desc', 'Underline');
14792 
14793 			// BlockFormat shortcuts keys
14794 			for (var i = 1; i <= 6; i++) {
14795 				ed.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
14796 			}
14797 
14798 			ed.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);
14799 			ed.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);
14800 			ed.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);
14801 		}
14802 
14803 		// Public functions
14804 
14805 		/**
14806 		 * Returns the format by name or all formats if no name is specified.
14807 		 *
14808 		 * @method get
14809 		 * @param {String} name Optional name to retrive by.
14810 		 * @return {Array/Object} Array/Object with all registred formats or a specific format.
14811 		 */
14812 		function get(name) {
14813 			return name ? formats[name] : formats;
14814 		}
14815 
14816 		/**
14817 		 * Registers a specific format by name.
14818 		 *
14819 		 * @method register
14820 		 * @param {Object/String} name Name of the format for example "bold".
14821 		 * @param {Object/Array} format Optional format object or array of format variants
14822 		 * can only be omitted if the first arg is an object.
14823 		 */
14824 		function register(name, format) {
14825 			if (name) {
14826 				if (typeof(name) !== 'string') {
14827 					each(name, function(format, name) {
14828 						register(name, format);
14829 					});
14830 				} else {
14831 					// Force format into array and add it to internal collection
14832 					format = format.length ? format : [format];
14833 
14834 					each(format, function(format) {
14835 						// Set deep to false by default on selector formats this to avoid removing
14836 						// alignment on images inside paragraphs when alignment is changed on paragraphs
14837 						if (format.deep === undef) {
14838 							format.deep = !format.selector;
14839 						}
14840 
14841 						// Default to true
14842 						if (format.split === undef) {
14843 							format.split = !format.selector || format.inline;
14844 						}
14845 
14846 						// Default to true
14847 						if (format.remove === undef && format.selector && !format.inline) {
14848 							format.remove = 'none';
14849 						}
14850 
14851 						// Mark format as a mixed format inline + block level
14852 						if (format.selector && format.inline) {
14853 							format.mixed = true;
14854 							format.block_expand = true;
14855 						}
14856 
14857 						// Split classes if needed
14858 						if (typeof(format.classes) === 'string') {
14859 							format.classes = format.classes.split(/\s+/);
14860 						}
14861 					});
14862 
14863 					formats[name] = format;
14864 				}
14865 			}
14866 		}
14867 
14868 		function getTextDecoration(node) {
14869 			var decoration;
14870 
14871 			ed.dom.getParent(node, function(n) {
14872 				decoration = ed.dom.getStyle(n, 'text-decoration');
14873 				return decoration && decoration !== 'none';
14874 			});
14875 
14876 			return decoration;
14877 		}
14878 
14879 		function processUnderlineAndColor(node) {
14880 			var textDecoration;
14881 			if (node.nodeType === 1 && node.parentNode && node.parentNode.nodeType === 1) {
14882 				textDecoration = getTextDecoration(node.parentNode);
14883 				if (ed.dom.getStyle(node, 'color') && textDecoration) {
14884 					ed.dom.setStyle(node, 'text-decoration', textDecoration);
14885 				} else if (ed.dom.getStyle(node, 'textdecoration') === textDecoration) {
14886 					ed.dom.setStyle(node, 'text-decoration', null);
14887 				}
14888 			}
14889 		}
14890 
14891 		/**
14892 		 * Applies the specified format to the current selection or specified node.
14893 		 *
14894 		 * @method apply
14895 		 * @param {String} name Name of format to apply.
14896 		 * @param {Object} vars Optional list of variables to replace within format before applying it.
14897 		 * @param {Node} node Optional node to apply the format to defaults to current selection.
14898 		 */
14899 		function apply(name, vars, node) {
14900 			var formatList = get(name), format = formatList[0], bookmark, rng, isCollapsed = !node && selection.isCollapsed();
14901 
14902 			function setElementFormat(elm, fmt) {
14903 				fmt = fmt || format;
14904 
14905 				if (elm) {
14906 					if (fmt.onformat) {
14907 						fmt.onformat(elm, fmt, vars, node);
14908 					}
14909 
14910 					each(fmt.styles, function(value, name) {
14911 						dom.setStyle(elm, name, replaceVars(value, vars));
14912 					});
14913 
14914 					// Needed for the WebKit span spam bug
14915 					// TODO: Remove this once WebKit/Blink fixes this
14916 					if (fmt.styles) {
14917 						var styleVal = dom.getAttrib(elm, 'style');
14918 
14919 						if (styleVal) {
14920 							elm.setAttribute('data-mce-style', styleVal);
14921 						}
14922 					}
14923 
14924 					each(fmt.attributes, function(value, name) {
14925 						dom.setAttrib(elm, name, replaceVars(value, vars));
14926 					});
14927 
14928 					each(fmt.classes, function(value) {
14929 						value = replaceVars(value, vars);
14930 
14931 						if (!dom.hasClass(elm, value)) {
14932 							dom.addClass(elm, value);
14933 						}
14934 					});
14935 				}
14936 			}
14937 
14938 			function adjustSelectionToVisibleSelection() {
14939 				function findSelectionEnd(start, end) {
14940 					var walker = new TreeWalker(end);
14941 					for (node = walker.current(); node; node = walker.prev()) {
14942 						if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') {
14943 							return node;
14944 						}
14945 					}
14946 				}
14947 
14948 				// Adjust selection so that a end container with a end offset of zero is not included in the selection
14949 				// as this isn't visible to the user.
14950 				var rng = ed.selection.getRng();
14951 				var start = rng.startContainer;
14952 				var end = rng.endContainer;
14953 
14954 				if (start != end && rng.endOffset === 0) {
14955 					var newEnd = findSelectionEnd(start, end);
14956 					var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
14957 
14958 					rng.setEnd(newEnd, endOffset);
14959 				}
14960 
14961 				return rng;
14962 			}
14963 
14964 			function applyStyleToList(node, bookmark, wrapElm, newWrappers, process){
14965 				var nodes = [], listIndex = -1, list, startIndex = -1, endIndex = -1, currentWrapElm;
14966 
14967 				// find the index of the first child list.
14968 				each(node.childNodes, function(n, index) {
14969 					if (n.nodeName === "UL" || n.nodeName === "OL") {
14970 						listIndex = index;
14971 						list = n;
14972 						return false;
14973 					}
14974 				});
14975 
14976 				// get the index of the bookmarks
14977 				each(node.childNodes, function(n, index) {
14978 					if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {
14979 						if (n.id == bookmark.id + "_start") {
14980 							startIndex = index;
14981 						} else if (n.id == bookmark.id + "_end") {
14982 							endIndex = index;
14983 						}
14984 					}
14985 				});
14986 
14987 				// if the selection spans across an embedded list, or there isn't an embedded list - handle processing normally
14988 				if (listIndex <= 0 || (startIndex < listIndex && endIndex > listIndex)) {
14989 					each(grep(node.childNodes), process);
14990 					return 0;
14991 				} else {
14992 					currentWrapElm = dom.clone(wrapElm, FALSE);
14993 
14994 					// create a list of the nodes on the same side of the list as the selection
14995 					each(grep(node.childNodes), function(n, index) {
14996 						if ((startIndex < listIndex && index < listIndex) || (startIndex > listIndex && index > listIndex)) {
14997 							nodes.push(n);
14998 							n.parentNode.removeChild(n);
14999 						}
15000 					});
15001 
15002 					// insert the wrapping element either before or after the list.
15003 					if (startIndex < listIndex) {
15004 						node.insertBefore(currentWrapElm, list);
15005 					} else if (startIndex > listIndex) {
15006 						node.insertBefore(currentWrapElm, list.nextSibling);
15007 					}
15008 
15009 					// add the new nodes to the list.
15010 					newWrappers.push(currentWrapElm);
15011 
15012 					each(nodes, function(node) {
15013 						currentWrapElm.appendChild(node);
15014 					});
15015 
15016 					return currentWrapElm;
15017 				}
15018 			}
15019 
15020 			function applyRngStyle(rng, bookmark, node_specific) {
15021 				var newWrappers = [], wrapName, wrapElm, contentEditable = true;
15022 
15023 				// Setup wrapper element
15024 				wrapName = format.inline || format.block;
15025 				wrapElm = dom.create(wrapName);
15026 				setElementFormat(wrapElm);
15027 
15028 				rangeUtils.walk(rng, function(nodes) {
15029 					var currentWrapElm;
15030 
15031 					/**
15032 					 * Process a list of nodes wrap them.
15033 					 */
15034 					function process(node) {
15035 						var nodeName, parentName, found, hasContentEditableState, lastContentEditable;
15036 
15037 						lastContentEditable = contentEditable;
15038 						nodeName = node.nodeName.toLowerCase();
15039 						parentName = node.parentNode.nodeName.toLowerCase();
15040 
15041 						// Node has a contentEditable value
15042 						if (node.nodeType === 1 && getContentEditable(node)) {
15043 							lastContentEditable = contentEditable;
15044 							contentEditable = getContentEditable(node) === "true";
15045 							hasContentEditableState = true; // We don't want to wrap the container only it's children
15046 						}
15047 
15048 						// Stop wrapping on br elements
15049 						if (isEq(nodeName, 'br')) {
15050 							currentWrapElm = 0;
15051 
15052 							// Remove any br elements when we wrap things
15053 							if (format.block) {
15054 								dom.remove(node);
15055 							}
15056 
15057 							return;
15058 						}
15059 
15060 						// If node is wrapper type
15061 						if (format.wrapper && matchNode(node, name, vars)) {
15062 							currentWrapElm = 0;
15063 							return;
15064 						}
15065 
15066 						// Can we rename the block
15067 						// TODO: Break this if up, too complex
15068 						if (contentEditable && !hasContentEditableState && format.block &&
15069 							!format.wrapper && isTextBlock(nodeName) && isValid(parentName, wrapName)) {
15070 							node = dom.rename(node, wrapName);
15071 							setElementFormat(node);
15072 							newWrappers.push(node);
15073 							currentWrapElm = 0;
15074 							return;
15075 						}
15076 
15077 						// Handle selector patterns
15078 						if (format.selector) {
15079 							// Look for matching formats
15080 							each(formatList, function(format) {
15081 								// Check collapsed state if it exists
15082 								if ('collapsed' in format && format.collapsed !== isCollapsed) {
15083 									return;
15084 								}
15085 
15086 								if (dom.is(node, format.selector) && !isCaretNode(node)) {
15087 									setElementFormat(node, format);
15088 									found = true;
15089 								}
15090 							});
15091 
15092 							// Continue processing if a selector match wasn't found and a inline element is defined
15093 							if (!format.inline || found) {
15094 								currentWrapElm = 0;
15095 								return;
15096 							}
15097 						}
15098 
15099 						// Is it valid to wrap this item
15100 						// TODO: Break this if up, too complex
15101 						if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
15102 								!(!node_specific && node.nodeType === 3 &&
15103 								node.nodeValue.length === 1 &&
15104 								node.nodeValue.charCodeAt(0) === 65279) &&
15105 								!isCaretNode(node) &&
15106 								(!format.inline || !isBlock(node))) {
15107 							// Start wrapping
15108 							if (!currentWrapElm) {
15109 								// Wrap the node
15110 								currentWrapElm = dom.clone(wrapElm, FALSE);
15111 								node.parentNode.insertBefore(currentWrapElm, node);
15112 								newWrappers.push(currentWrapElm);
15113 							}
15114 
15115 							currentWrapElm.appendChild(node);
15116 						} else if (nodeName == 'li' && bookmark) {
15117 							// Start wrapping - if we are in a list node and have a bookmark, then
15118 							// we will always begin by wrapping in a new element.
15119 							currentWrapElm = applyStyleToList(node, bookmark, wrapElm, newWrappers, process);
15120 						} else {
15121 							// Start a new wrapper for possible children
15122 							currentWrapElm = 0;
15123 
15124 							each(grep(node.childNodes), process);
15125 
15126 							if (hasContentEditableState) {
15127 								contentEditable = lastContentEditable; // Restore last contentEditable state from stack
15128 							}
15129 
15130 							// End the last wrapper
15131 							currentWrapElm = 0;
15132 						}
15133 					}
15134 
15135 					// Process siblings from range
15136 					each(nodes, process);
15137 				});
15138 
15139 				// Wrap links inside as well, for example color inside a link when the wrapper is around the link
15140 				if (format.wrap_links === false) {
15141 					each(newWrappers, function(node) {
15142 						function process(node) {
15143 							var i, currentWrapElm, children;
15144 
15145 							if (node.nodeName === 'A') {
15146 								currentWrapElm = dom.clone(wrapElm, FALSE);
15147 								newWrappers.push(currentWrapElm);
15148 
15149 								children = grep(node.childNodes);
15150 								for (i = 0; i < children.length; i++) {
15151 									currentWrapElm.appendChild(children[i]);
15152 								}
15153 
15154 								node.appendChild(currentWrapElm);
15155 							}
15156 
15157 							each(grep(node.childNodes), process);
15158 						}
15159 
15160 						process(node);
15161 					});
15162 				}
15163 
15164 				// Cleanup
15165 				each(newWrappers, function(node) {
15166 					var childCount;
15167 
15168 					function getChildCount(node) {
15169 						var count = 0;
15170 
15171 						each(node.childNodes, function(node) {
15172 							if (!isWhiteSpaceNode(node) && !isBookmarkNode(node)) {
15173 								count++;
15174 							}
15175 						});
15176 
15177 						return count;
15178 					}
15179 
15180 					function mergeStyles(node) {
15181 						var child, clone;
15182 
15183 						each(node.childNodes, function(node) {
15184 							if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {
15185 								child = node;
15186 								return FALSE; // break loop
15187 							}
15188 						});
15189 
15190 						// If child was found and of the same type as the current node
15191 						if (child && !isBookmarkNode(child) && matchName(child, format)) {
15192 							clone = dom.clone(child, FALSE);
15193 							setElementFormat(clone);
15194 
15195 							dom.replace(clone, node, TRUE);
15196 							dom.remove(child, 1);
15197 						}
15198 
15199 						return clone || node;
15200 					}
15201 
15202 					childCount = getChildCount(node);
15203 
15204 					// Remove empty nodes but only if there is multiple wrappers and they are not block
15205 					// elements so never remove single <h1></h1> since that would remove the
15206 					// currrent empty block element where the caret is at
15207 					if ((newWrappers.length > 1 || !isBlock(node)) && childCount === 0) {
15208 						dom.remove(node, 1);
15209 						return;
15210 					}
15211 
15212 					if (format.inline || format.wrapper) {
15213 						// Merges the current node with it's children of similar type to reduce the number of elements
15214 						if (!format.exact && childCount === 1) {
15215 							node = mergeStyles(node);
15216 						}
15217 
15218 						// Remove/merge children
15219 						each(formatList, function(format) {
15220 							// Merge all children of similar type will move styles from child to parent
15221 							// this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>
15222 							// will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>
15223 							each(dom.select(format.inline, node), function(child) {
15224 								var parent;
15225 
15226 								if (isBookmarkNode(child)) {
15227 									return;
15228 								}
15229 
15230 								// When wrap_links is set to false we don't want
15231 								// to remove the format on children within links
15232 								if (format.wrap_links === false) {
15233 									parent = child.parentNode;
15234 
15235 									do {
15236 										if (parent.nodeName === 'A') {
15237 											return;
15238 										}
15239 									} while ((parent = parent.parentNode));
15240 								}
15241 
15242 								removeFormat(format, vars, child, format.exact ? child : null);
15243 							});
15244 						});
15245 
15246 						// Remove child if direct parent is of same type
15247 						if (matchNode(node.parentNode, name, vars)) {
15248 							dom.remove(node, 1);
15249 							node = 0;
15250 							return TRUE;
15251 						}
15252 
15253 						// Look for parent with similar style format
15254 						if (format.merge_with_parents) {
15255 							dom.getParent(node.parentNode, function(parent) {
15256 								if (matchNode(parent, name, vars)) {
15257 									dom.remove(node, 1);
15258 									node = 0;
15259 									return TRUE;
15260 								}
15261 							});
15262 						}
15263 
15264 						// Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
15265 						if (node && format.merge_siblings !== false) {
15266 							node = mergeSiblings(getNonWhiteSpaceSibling(node), node);
15267 							node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));
15268 						}
15269 					}
15270 				});
15271 			}
15272 
15273 			if (format) {
15274 				if (node) {
15275 					if (node.nodeType) {
15276 						rng = dom.createRng();
15277 						rng.setStartBefore(node);
15278 						rng.setEndAfter(node);
15279 						applyRngStyle(expandRng(rng, formatList), null, true);
15280 					} else {
15281 						applyRngStyle(node, null, true);
15282 					}
15283 				} else {
15284 					if (!isCollapsed || !format.inline || dom.select('td.mce-item-selected,th.mce-item-selected').length) {
15285 						// Obtain selection node before selection is unselected by applyRngStyle()
15286 						var curSelNode = ed.selection.getNode();
15287 
15288 						// If the formats have a default block and we can't find a parent block then
15289 						// start wrapping it with a DIV this is for forced_root_blocks: false
15290 						// It's kind of a hack but people should be using the default block type P since all desktop editors work that way
15291 						if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) {
15292 							apply(formatList[0].defaultBlock);
15293 						}
15294 
15295 						// Apply formatting to selection
15296 						ed.selection.setRng(adjustSelectionToVisibleSelection());
15297 						bookmark = selection.getBookmark();
15298 						applyRngStyle(expandRng(selection.getRng(TRUE), formatList), bookmark);
15299 
15300 						// Colored nodes should be underlined so that the color of the underline matches the text color.
15301 						if (format.styles && (format.styles.color || format.styles.textDecoration)) {
15302 							walk(curSelNode, processUnderlineAndColor, 'childNodes');
15303 							processUnderlineAndColor(curSelNode);
15304 						}
15305 
15306 						selection.moveToBookmark(bookmark);
15307 						moveStart(selection.getRng(TRUE));
15308 						ed.nodeChanged();
15309 					} else {
15310 						performCaretAction('apply', name, vars);
15311 					}
15312 				}
15313 			}
15314 		}
15315 
15316 		/**
15317 		 * Removes the specified format from the current selection or specified node.
15318 		 *
15319 		 * @method remove
15320 		 * @param {String} name Name of format to remove.
15321 		 * @param {Object} vars Optional list of variables to replace within format before removing it.
15322 		 * @param {Node/Range} node Optional node or DOM range to remove the format from defaults to current selection.
15323 		 */
15324 		function remove(name, vars, node) {
15325 			var formatList = get(name), format = formatList[0], bookmark, rng, contentEditable = true;
15326 
15327 			// Merges the styles for each node
15328 			function process(node) {
15329 				var children, i, l, lastContentEditable, hasContentEditableState;
15330 
15331 				// Node has a contentEditable value
15332 				if (node.nodeType === 1 && getContentEditable(node)) {
15333 					lastContentEditable = contentEditable;
15334 					contentEditable = getContentEditable(node) === "true";
15335 					hasContentEditableState = true; // We don't want to wrap the container only it's children
15336 				}
15337 
15338 				// Grab the children first since the nodelist might be changed
15339 				children = grep(node.childNodes);
15340 
15341 				// Process current node
15342 				if (contentEditable && !hasContentEditableState) {
15343 					for (i = 0, l = formatList.length; i < l; i++) {
15344 						if (removeFormat(formatList[i], vars, node, node)) {
15345 							break;
15346 						}
15347 					}
15348 				}
15349 
15350 				// Process the children
15351 				if (format.deep) {
15352 					if (children.length) {
15353 						for (i = 0, l = children.length; i < l; i++) {
15354 							process(children[i]);
15355 						}
15356 
15357 						if (hasContentEditableState) {
15358 							contentEditable = lastContentEditable; // Restore last contentEditable state from stack
15359 						}
15360 					}
15361 				}
15362 			}
15363 
15364 			function findFormatRoot(container) {
15365 				var formatRoot;
15366 
15367 				// Find format root
15368 				each(getParents(container.parentNode).reverse(), function(parent) {
15369 					var format;
15370 
15371 					// Find format root element
15372 					if (!formatRoot && parent.id != '_start' && parent.id != '_end') {
15373 						// Is the node matching the format we are looking for
15374 						format = matchNode(parent, name, vars);
15375 						if (format && format.split !== false) {
15376 							formatRoot = parent;
15377 						}
15378 					}
15379 				});
15380 
15381 				return formatRoot;
15382 			}
15383 
15384 			function wrapAndSplit(format_root, container, target, split) {
15385 				var parent, clone, lastClone, firstClone, i, formatRootParent;
15386 
15387 				// Format root found then clone formats and split it
15388 				if (format_root) {
15389 					formatRootParent = format_root.parentNode;
15390 
15391 					for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {
15392 						clone = dom.clone(parent, FALSE);
15393 
15394 						for (i = 0; i < formatList.length; i++) {
15395 							if (removeFormat(formatList[i], vars, clone, clone)) {
15396 								clone = 0;
15397 								break;
15398 							}
15399 						}
15400 
15401 						// Build wrapper node
15402 						if (clone) {
15403 							if (lastClone) {
15404 								clone.appendChild(lastClone);
15405 							}
15406 
15407 							if (!firstClone) {
15408 								firstClone = clone;
15409 							}
15410 
15411 							lastClone = clone;
15412 						}
15413 					}
15414 
15415 					// Never split block elements if the format is mixed
15416 					if (split && (!format.mixed || !isBlock(format_root))) {
15417 						container = dom.split(format_root, container);
15418 					}
15419 
15420 					// Wrap container in cloned formats
15421 					if (lastClone) {
15422 						target.parentNode.insertBefore(lastClone, target);
15423 						firstClone.appendChild(target);
15424 					}
15425 				}
15426 
15427 				return container;
15428 			}
15429 
15430 			function splitToFormatRoot(container) {
15431 				return wrapAndSplit(findFormatRoot(container), container, container, true);
15432 			}
15433 
15434 			function unwrap(start) {
15435 				var node = dom.get(start ? '_start' : '_end'),
15436 					out = node[start ? 'firstChild' : 'lastChild'];
15437 
15438 				// If the end is placed within the start the result will be removed
15439 				// So this checks if the out node is a bookmark node if it is it
15440 				// checks for another more suitable node
15441 				if (isBookmarkNode(out)) {
15442 					out = out[start ? 'firstChild' : 'lastChild'];
15443 				}
15444 
15445 				dom.remove(node, true);
15446 
15447 				return out;
15448 			}
15449 
15450 			function removeRngStyle(rng) {
15451 				var startContainer, endContainer;
15452 				var commonAncestorContainer = rng.commonAncestorContainer;
15453 
15454 				rng = expandRng(rng, formatList, TRUE);
15455 
15456 				if (format.split) {
15457 					startContainer = getContainer(rng, TRUE);
15458 					endContainer = getContainer(rng);
15459 
15460 					if (startContainer != endContainer) {
15461 						// WebKit will render the table incorrectly if we wrap a TH or TD in a SPAN
15462 						// so let's see if we can use the first child instead
15463 						// This will happen if you triple click a table cell and use remove formatting
15464 						if (/^(TR|TH|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) {
15465 							if (startContainer.nodeName == "TR") {
15466 								startContainer = startContainer.firstChild.firstChild || startContainer;
15467 							} else {
15468 								startContainer = startContainer.firstChild || startContainer;
15469 							}
15470 						}
15471 
15472 						// Try to adjust endContainer as well if cells on the same row were selected - bug #6410
15473 						if (commonAncestorContainer &&
15474 							/^T(HEAD|BODY|FOOT|R)$/.test(commonAncestorContainer.nodeName) &&
15475 							/^(TH|TD)$/.test(endContainer.nodeName) && endContainer.firstChild) {
15476 							endContainer = endContainer.firstChild || endContainer;
15477 						}
15478 
15479 						// Wrap start/end nodes in span element since these might be cloned/moved
15480 						startContainer = wrap(startContainer, 'span', {id: '_start', 'data-mce-type': 'bookmark'});
15481 						endContainer = wrap(endContainer, 'span', {id: '_end', 'data-mce-type': 'bookmark'});
15482 
15483 						// Split start/end
15484 						splitToFormatRoot(startContainer);
15485 						splitToFormatRoot(endContainer);
15486 
15487 						// Unwrap start/end to get real elements again
15488 						startContainer = unwrap(TRUE);
15489 						endContainer = unwrap();
15490 					} else {
15491 						startContainer = endContainer = splitToFormatRoot(startContainer);
15492 					}
15493 
15494 					// Update range positions since they might have changed after the split operations
15495 					rng.startContainer = startContainer.parentNode;
15496 					rng.startOffset = nodeIndex(startContainer);
15497 					rng.endContainer = endContainer.parentNode;
15498 					rng.endOffset = nodeIndex(endContainer) + 1;
15499 				}
15500 
15501 				// Remove items between start/end
15502 				rangeUtils.walk(rng, function(nodes) {
15503 					each(nodes, function(node) {
15504 						process(node);
15505 
15506 						// Remove parent span if it only contains text-decoration: underline, yet a parent node is also underlined.
15507 						if (node.nodeType === 1 && ed.dom.getStyle(node, 'text-decoration') === 'underline' &&
15508 							node.parentNode && getTextDecoration(node.parentNode) === 'underline') {
15509 							removeFormat({
15510 								'deep': false,
15511 								'exact': true,
15512 								'inline': 'span',
15513 								'styles': {
15514 									'textDecoration': 'underline'
15515 								}
15516 							}, null, node);
15517 						}
15518 					});
15519 				});
15520 			}
15521 
15522 			// Handle node
15523 			if (node) {
15524 				if (node.nodeType) {
15525 					rng = dom.createRng();
15526 					rng.setStartBefore(node);
15527 					rng.setEndAfter(node);
15528 					removeRngStyle(rng);
15529 				} else {
15530 					removeRngStyle(node);
15531 				}
15532 
15533 				return;
15534 			}
15535 
15536 			if (!selection.isCollapsed() || !format.inline || dom.select('td.mce-item-selected,th.mce-item-selected').length) {
15537 				bookmark = selection.getBookmark();
15538 				removeRngStyle(selection.getRng(TRUE));
15539 				selection.moveToBookmark(bookmark);
15540 
15541 				// Check if start element still has formatting then we are at: "<b>text|</b>text"
15542 				// and need to move the start into the next text node
15543 				if (format.inline && match(name, vars, selection.getStart())) {
15544 					moveStart(selection.getRng(true));
15545 				}
15546 
15547 				ed.nodeChanged();
15548 			} else {
15549 				performCaretAction('remove', name, vars);
15550 			}
15551 		}
15552 
15553 		/**
15554 		 * Toggles the specified format on/off.
15555 		 *
15556 		 * @method toggle
15557 		 * @param {String} name Name of format to apply/remove.
15558 		 * @param {Object} vars Optional list of variables to replace within format before applying/removing it.
15559 		 * @param {Node} node Optional node to apply the format to or remove from. Defaults to current selection.
15560 		 */
15561 		function toggle(name, vars, node) {
15562 			var fmt = get(name);
15563 
15564 			if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle)) {
15565 				remove(name, vars, node);
15566 			} else {
15567 				apply(name, vars, node);
15568 			}
15569 		}
15570 
15571 		/**
15572 		 * Return true/false if the specified node has the specified format.
15573 		 *
15574 		 * @method matchNode
15575 		 * @param {Node} node Node to check the format on.
15576 		 * @param {String} name Format name to check.
15577 		 * @param {Object} vars Optional list of variables to replace before checking it.
15578 		 * @param {Boolean} similar Match format that has similar properties.
15579 		 * @return {Object} Returns the format object it matches or undefined if it doesn't match.
15580 		 */
15581 		function matchNode(node, name, vars, similar) {
15582 			var formatList = get(name), format, i, classes;
15583 
15584 			function matchItems(node, format, item_name) {
15585 				var key, value, items = format[item_name], i;
15586 
15587 				// Custom match
15588 				if (format.onmatch) {
15589 					return format.onmatch(node, format, item_name);
15590 				}
15591 
15592 				// Check all items
15593 				if (items) {
15594 					// Non indexed object
15595 					if (items.length === undef) {
15596 						for (key in items) {
15597 							if (items.hasOwnProperty(key)) {
15598 								if (item_name === 'attributes') {
15599 									value = dom.getAttrib(node, key);
15600 								} else {
15601 									value = getStyle(node, key);
15602 								}
15603 
15604 								if (similar && !value && !format.exact) {
15605 									return;
15606 								}
15607 
15608 								if ((!similar || format.exact) && !isEq(value, normalizeStyleValue(replaceVars(items[key], vars), key))) {
15609 									return;
15610 								}
15611 							}
15612 						}
15613 					} else {
15614 						// Only one match needed for indexed arrays
15615 						for (i = 0; i < items.length; i++) {
15616 							if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i])) {
15617 								return format;
15618 							}
15619 						}
15620 					}
15621 				}
15622 
15623 				return format;
15624 			}
15625 
15626 			if (formatList && node) {
15627 				// Check each format in list
15628 				for (i = 0; i < formatList.length; i++) {
15629 					format = formatList[i];
15630 
15631 					// Name name, attributes, styles and classes
15632 					if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) {
15633 						// Match classes
15634 						if ((classes = format.classes)) {
15635 							for (i = 0; i < classes.length; i++) {
15636 								if (!dom.hasClass(node, classes[i])) {
15637 									return;
15638 								}
15639 							}
15640 						}
15641 
15642 						return format;
15643 					}
15644 				}
15645 			}
15646 		}
15647 
15648 		/**
15649 		 * Matches the current selection or specified node against the specified format name.
15650 		 *
15651 		 * @method match
15652 		 * @param {String} name Name of format to match.
15653 		 * @param {Object} vars Optional list of variables to replace before checking it.
15654 		 * @param {Node} node Optional node to check.
15655 		 * @return {boolean} true/false if the specified selection/node matches the format.
15656 		 */
15657 		function match(name, vars, node) {
15658 			var startNode;
15659 
15660 			function matchParents(node) {
15661 				var root = dom.getRoot();
15662 
15663 				if (node === root) {
15664 					return false;
15665 				}
15666 
15667 				// Find first node with similar format settings
15668 				node = dom.getParent(node, function(node) {
15669 					return node.parentNode === root || !!matchNode(node, name, vars, true);
15670 				});
15671 
15672 				// Do an exact check on the similar format element
15673 				return matchNode(node, name, vars);
15674 			}
15675 
15676 			// Check specified node
15677 			if (node) {
15678 				return matchParents(node);
15679 			}
15680 
15681 			// Check selected node
15682 			node = selection.getNode();
15683 			if (matchParents(node)) {
15684 				return TRUE;
15685 			}
15686 
15687 			// Check start node if it's different
15688 			startNode = selection.getStart();
15689 			if (startNode != node) {
15690 				if (matchParents(startNode)) {
15691 					return TRUE;
15692 				}
15693 			}
15694 
15695 			return FALSE;
15696 		}
15697 
15698 		/**
15699 		 * Matches the current selection against the array of formats and returns a new array with matching formats.
15700 		 *
15701 		 * @method matchAll
15702 		 * @param {Array} names Name of format to match.
15703 		 * @param {Object} vars Optional list of variables to replace before checking it.
15704 		 * @return {Array} Array with matched formats.
15705 		 */
15706 		function matchAll(names, vars) {
15707 			var startElement, matchedFormatNames = [], checkedMap = {};
15708 
15709 			// Check start of selection for formats
15710 			startElement = selection.getStart();
15711 			dom.getParent(startElement, function(node) {
15712 				var i, name;
15713 
15714 				for (i = 0; i < names.length; i++) {
15715 					name = names[i];
15716 
15717 					if (!checkedMap[name] && matchNode(node, name, vars)) {
15718 						checkedMap[name] = true;
15719 						matchedFormatNames.push(name);
15720 					}
15721 				}
15722 			}, dom.getRoot());
15723 
15724 			return matchedFormatNames;
15725 		}
15726 
15727 		/**
15728 		 * Returns true/false if the specified format can be applied to the current selection or not. It
15729 		 * will currently only check the state for selector formats, it returns true on all other format types.
15730 		 *
15731 		 * @method canApply
15732 		 * @param {String} name Name of format to check.
15733 		 * @return {boolean} true/false if the specified format can be applied to the current selection/node.
15734 		 */
15735 		function canApply(name) {
15736 			var formatList = get(name), startNode, parents, i, x, selector;
15737 
15738 			if (formatList) {
15739 				startNode = selection.getStart();
15740 				parents = getParents(startNode);
15741 
15742 				for (x = formatList.length - 1; x >= 0; x--) {
15743 					selector = formatList[x].selector;
15744 
15745 					// Format is not selector based then always return TRUE
15746 					// Is it has a defaultBlock then it's likely it can be applied for example align on a non block element line
15747 					if (!selector || formatList[x].defaultBlock) {
15748 						return TRUE;
15749 					}
15750 
15751 					for (i = parents.length - 1; i >= 0; i--) {
15752 						if (dom.is(parents[i], selector)) {
15753 							return TRUE;
15754 						}
15755 					}
15756 				}
15757 			}
15758 
15759 			return FALSE;
15760 		}
15761 
15762 		/**
15763 		 * Executes the specified callback when the current selection matches the formats or not.
15764 		 *
15765 		 * @method formatChanged
15766 		 * @param {String} formats Comma separated list of formats to check for.
15767 		 * @param {function} callback Callback with state and args when the format is changed/toggled on/off.
15768 		 * @param {Boolean} similar True/false state if the match should handle similar or exact formats.
15769 		 */
15770 		function formatChanged(formats, callback, similar) {
15771 			var currentFormats;
15772 
15773 			// Setup format node change logic
15774 			if (!formatChangeData) {
15775 				formatChangeData = {};
15776 				currentFormats = {};
15777 
15778 				ed.on('NodeChange', function(e) {
15779 					var parents = getParents(e.element), matchedFormats = {};
15780 
15781 					// Check for new formats
15782 					each(formatChangeData, function(callbacks, format) {
15783 						each(parents, function(node) {
15784 							if (matchNode(node, format, {}, callbacks.similar)) {
15785 								if (!currentFormats[format]) {
15786 									// Execute callbacks
15787 									each(callbacks, function(callback) {
15788 										callback(true, {node: node, format: format, parents: parents});
15789 									});
15790 
15791 									currentFormats[format] = callbacks;
15792 								}
15793 
15794 								matchedFormats[format] = callbacks;
15795 								return false;
15796 							}
15797 						});
15798 					});
15799 
15800 					// Check if current formats still match
15801 					each(currentFormats, function(callbacks, format) {
15802 						if (!matchedFormats[format]) {
15803 							delete currentFormats[format];
15804 
15805 							each(callbacks, function(callback) {
15806 								callback(false, {node: e.element, format: format, parents: parents});
15807 							});
15808 						}
15809 					});
15810 				});
15811 			}
15812 
15813 			// Add format listeners
15814 			each(formats.split(','), function(format) {
15815 				if (!formatChangeData[format]) {
15816 					formatChangeData[format] = [];
15817 					formatChangeData[format].similar = similar;
15818 				}
15819 
15820 				formatChangeData[format].push(callback);
15821 			});
15822 
15823 			return this;
15824 		}
15825 
15826 		/**
15827 		 * Returns a preview css text for the specified format.
15828 		 *
15829 		 * @method getCssText
15830 		 * @param {String/Object} format Format to generate preview css text for.
15831 		 * @return {String} Css text for the specified format.
15832 		 * @example
15833 		 * var cssText1 = editor.formatter.getCssText('bold');
15834 		 * var cssText2 = editor.formatter.getCssText({inline: 'b'});
15835 		 */
15836 		function getCssText(format) {
15837 			return Preview.getCssText(ed, format);
15838 		}
15839 
15840 		// Expose to public
15841 		extend(this, {
15842 			get: get,
15843 			register: register,
15844 			apply: apply,
15845 			remove: remove,
15846 			toggle: toggle,
15847 			match: match,
15848 			matchAll: matchAll,
15849 			matchNode: matchNode,
15850 			canApply: canApply,
15851 			formatChanged: formatChanged,
15852 			getCssText: getCssText
15853 		});
15854 
15855 		// Initialize
15856 		defaultFormats();
15857 		addKeyboardShortcuts();
15858 		ed.on('BeforeGetContent', function() {
15859 			if (markCaretContainersBogus) {
15860 				markCaretContainersBogus();
15861 			}
15862 		});
15863 		ed.on('mouseup keydown', function(e) {
15864 			if (disableCaretContainer) {
15865 				disableCaretContainer(e);
15866 			}
15867 		});
15868 
15869 		// Private functions
15870 
15871 		/**
15872 		 * Checks if the specified nodes name matches the format inline/block or selector.
15873 		 *
15874 		 * @private
15875 		 * @param {Node} node Node to match against the specified format.
15876 		 * @param {Object} format Format object o match with.
15877 		 * @return {boolean} true/false if the format matches.
15878 		 */
15879 		function matchName(node, format) {
15880 			// Check for inline match
15881 			if (isEq(node, format.inline)) {
15882 				return TRUE;
15883 			}
15884 
15885 			// Check for block match
15886 			if (isEq(node, format.block)) {
15887 				return TRUE;
15888 			}
15889 
15890 			// Check for selector match
15891 			if (format.selector) {
15892 				return node.nodeType == 1 && dom.is(node, format.selector);
15893 			}
15894 		}
15895 
15896 		/**
15897 		 * Compares two string/nodes regardless of their case.
15898 		 *
15899 		 * @private
15900 		 * @param {String/Node} Node or string to compare.
15901 		 * @param {String/Node} Node or string to compare.
15902 		 * @return {boolean} True/false if they match.
15903 		 */
15904 		function isEq(str1, str2) {
15905 			str1 = str1 || '';
15906 			str2 = str2 || '';
15907 
15908 			str1 = '' + (str1.nodeName || str1);
15909 			str2 = '' + (str2.nodeName || str2);
15910 
15911 			return str1.toLowerCase() == str2.toLowerCase();
15912 		}
15913 
15914 		/**
15915 		 * Returns the style by name on the specified node. This method modifies the style
15916 		 * contents to make it more easy to match. This will resolve a few browser issues.
15917 		 *
15918 		 * @private
15919 		 * @param {Node} node to get style from.
15920 		 * @param {String} name Style name to get.
15921 		 * @return {String} Style item value.
15922 		 */
15923 		function getStyle(node, name) {
15924 			return normalizeStyleValue(dom.getStyle(node, name), name);
15925 		}
15926 
15927 		/**
15928 		 * Normalize style value by name. This method modifies the style contents
15929 		 * to make it more easy to match. This will resolve a few browser issues.
15930 		 *
15931 		 * @private
15932 		 * @param {Node} node to get style from.
15933 		 * @param {String} name Style name to get.
15934 		 * @return {String} Style item value.
15935 		 */
15936 		function normalizeStyleValue(value, name) {
15937 			// Force the format to hex
15938 			if (name == 'color' || name == 'backgroundColor') {
15939 				value = dom.toHex(value);
15940 			}
15941 
15942 			// Opera will return bold as 700
15943 			if (name == 'fontWeight' && value == 700) {
15944 				value = 'bold';
15945 			}
15946 
15947 			// Normalize fontFamily so "'Font name', Font" becomes: "Font name,Font"
15948 			if (name == 'fontFamily') {
15949 				value = value.replace(/[\'\"]/g, '').replace(/,\s+/g, ',');
15950 			}
15951 
15952 			return '' + value;
15953 		}
15954 
15955 		/**
15956 		 * Replaces variables in the value. The variable format is %var.
15957 		 *
15958 		 * @private
15959 		 * @param {String} value Value to replace variables in.
15960 		 * @param {Object} vars Name/value array with variables to replace.
15961 		 * @return {String} New value with replaced variables.
15962 		 */
15963 		function replaceVars(value, vars) {
15964 			if (typeof(value) != "string") {
15965 				value = value(vars);
15966 			} else if (vars) {
15967 				value = value.replace(/%(\w+)/g, function(str, name) {
15968 					return vars[name] || str;
15969 				});
15970 			}
15971 
15972 			return value;
15973 		}
15974 
15975 		function isWhiteSpaceNode(node) {
15976 			return node && node.nodeType === 3 && /^([\t \r\n]+|)$/.test(node.nodeValue);
15977 		}
15978 
15979 		function wrap(node, name, attrs) {
15980 			var wrapper = dom.create(name, attrs);
15981 
15982 			node.parentNode.insertBefore(wrapper, node);
15983 			wrapper.appendChild(node);
15984 
15985 			return wrapper;
15986 		}
15987 
15988 		/**
15989 		 * Expands the specified range like object to depending on format.
15990 		 *
15991 		 * For example on block formats it will move the start/end position
15992 		 * to the beginning of the current block.
15993 		 *
15994 		 * @private
15995 		 * @param {Object} rng Range like object.
15996 		 * @param {Array} formats Array with formats to expand by.
15997 		 * @return {Object} Expanded range like object.
15998 		 */
15999 		function expandRng(rng, format, remove) {
16000 			var lastIdx, leaf, endPoint,
16001 				startContainer = rng.startContainer,
16002 				startOffset = rng.startOffset,
16003 				endContainer = rng.endContainer,
16004 				endOffset = rng.endOffset;
16005 
16006 			// This function walks up the tree if there is no siblings before/after the node
16007 			function findParentContainer(start) {
16008 				var container, parent, sibling, siblingName, root;
16009 
16010 				container = parent = start ? startContainer : endContainer;
16011 				siblingName = start ? 'previousSibling' : 'nextSibling';
16012 				root = dom.getRoot();
16013 
16014 				function isBogusBr(node) {
16015 					return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling;
16016 				}
16017 
16018 				// If it's a text node and the offset is inside the text
16019 				if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {
16020 					if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {
16021 						return container;
16022 					}
16023 				}
16024 
16025 				/*eslint no-constant-condition:0 */
16026 				while (true) {
16027 					// Stop expanding on block elements
16028 					if (!format[0].block_expand && isBlock(parent)) {
16029 						return parent;
16030 					}
16031 
16032 					// Walk left/right
16033 					for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
16034 						if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) {
16035 							return parent;
16036 						}
16037 					}
16038 
16039 					// Check if we can move up are we at root level or body level
16040 					if (parent.parentNode == root) {
16041 						container = parent;
16042 						break;
16043 					}
16044 
16045 					parent = parent.parentNode;
16046 				}
16047 
16048 				return container;
16049 			}
16050 
16051 			// This function walks down the tree to find the leaf at the selection.
16052 			// The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.
16053 			function findLeaf(node, offset) {
16054 				if (offset === undef) {
16055 					offset = node.nodeType === 3 ? node.length : node.childNodes.length;
16056 				}
16057 
16058 				while (node && node.hasChildNodes()) {
16059 					node = node.childNodes[offset];
16060 					if (node) {
16061 						offset = node.nodeType === 3 ? node.length : node.childNodes.length;
16062 					}
16063 				}
16064 				return { node: node, offset: offset };
16065 			}
16066 
16067 			// If index based start position then resolve it
16068 			if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
16069 				lastIdx = startContainer.childNodes.length - 1;
16070 				startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset];
16071 
16072 				if (startContainer.nodeType == 3) {
16073 					startOffset = 0;
16074 				}
16075 			}
16076 
16077 			// If index based end position then resolve it
16078 			if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
16079 				lastIdx = endContainer.childNodes.length - 1;
16080 				endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1];
16081 
16082 				if (endContainer.nodeType == 3) {
16083 					endOffset = endContainer.nodeValue.length;
16084 				}
16085 			}
16086 
16087 			// Expands the node to the closes contentEditable false element if it exists
16088 			function findParentContentEditable(node) {
16089 				var parent = node;
16090 
16091 				while (parent) {
16092 					if (parent.nodeType === 1 && getContentEditable(parent)) {
16093 						return getContentEditable(parent) === "false" ? parent : node;
16094 					}
16095 
16096 					parent = parent.parentNode;
16097 				}
16098 
16099 				return node;
16100 			}
16101 
16102 			function findWordEndPoint(container, offset, start) {
16103 				var walker, node, pos, lastTextNode;
16104 
16105 				function findSpace(node, offset) {
16106 					var pos, pos2, str = node.nodeValue;
16107 
16108 					if (typeof(offset) == "undefined") {
16109 						offset = start ? str.length : 0;
16110 					}
16111 
16112 					if (start) {
16113 						pos = str.lastIndexOf(' ', offset);
16114 						pos2 = str.lastIndexOf('\u00a0', offset);
16115 						pos = pos > pos2 ? pos : pos2;
16116 
16117 						// Include the space on remove to avoid tag soup
16118 						if (pos !== -1 && !remove) {
16119 							pos++;
16120 						}
16121 					} else {
16122 						pos = str.indexOf(' ', offset);
16123 						pos2 = str.indexOf('\u00a0', offset);
16124 						pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
16125 					}
16126 
16127 					return pos;
16128 				}
16129 
16130 				if (container.nodeType === 3) {
16131 					pos = findSpace(container, offset);
16132 
16133 					if (pos !== -1) {
16134 						return {container: container, offset: pos};
16135 					}
16136 
16137 					lastTextNode = container;
16138 				}
16139 
16140 				// Walk the nodes inside the block
16141 				walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
16142 				while ((node = walker[start ? 'prev' : 'next']())) {
16143 					if (node.nodeType === 3) {
16144 						lastTextNode = node;
16145 						pos = findSpace(node);
16146 
16147 						if (pos !== -1) {
16148 							return {container: node, offset: pos};
16149 						}
16150 					} else if (isBlock(node)) {
16151 						break;
16152 					}
16153 				}
16154 
16155 				if (lastTextNode) {
16156 					if (start) {
16157 						offset = 0;
16158 					} else {
16159 						offset = lastTextNode.length;
16160 					}
16161 
16162 					return {container: lastTextNode, offset: offset};
16163 				}
16164 			}
16165 
16166 			function findSelectorEndPoint(container, sibling_name) {
16167 				var parents, i, y, curFormat;
16168 
16169 				if (container.nodeType == 3 && container.nodeValue.length === 0 && container[sibling_name]) {
16170 					container = container[sibling_name];
16171 				}
16172 
16173 				parents = getParents(container);
16174 				for (i = 0; i < parents.length; i++) {
16175 					for (y = 0; y < format.length; y++) {
16176 						curFormat = format[y];
16177 
16178 						// If collapsed state is set then skip formats that doesn't match that
16179 						if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed) {
16180 							continue;
16181 						}
16182 
16183 						if (dom.is(parents[i], curFormat.selector)) {
16184 							return parents[i];
16185 						}
16186 					}
16187 				}
16188 
16189 				return container;
16190 			}
16191 
16192 			function findBlockEndPoint(container, sibling_name) {
16193 				var node, root = dom.getRoot();
16194 
16195 				// Expand to block of similar type
16196 				if (!format[0].wrapper) {
16197 					node = dom.getParent(container, format[0].block, root);
16198 				}
16199 
16200 				// Expand to first wrappable block element or any block element
16201 				if (!node) {
16202 					node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, function(node) {
16203 						// Fixes #6183 where it would expand to editable parent element in inline mode
16204 						return node != root && isTextBlock(node);
16205 					});
16206 				}
16207 
16208 				// Exclude inner lists from wrapping
16209 				if (node && format[0].wrapper) {
16210 					node = getParents(node, 'ul,ol').reverse()[0] || node;
16211 				}
16212 
16213 				// Didn't find a block element look for first/last wrappable element
16214 				if (!node) {
16215 					node = container;
16216 
16217 					while (node[sibling_name] && !isBlock(node[sibling_name])) {
16218 						node = node[sibling_name];
16219 
16220 						// Break on BR but include it will be removed later on
16221 						// we can't remove it now since we need to check if it can be wrapped
16222 						if (isEq(node, 'br')) {
16223 							break;
16224 						}
16225 					}
16226 				}
16227 
16228 				return node || container;
16229 			}
16230 
16231 			// Expand to closest contentEditable element
16232 			startContainer = findParentContentEditable(startContainer);
16233 			endContainer = findParentContentEditable(endContainer);
16234 
16235 			// Exclude bookmark nodes if possible
16236 			if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
16237 				startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
16238 				startContainer = startContainer.nextSibling || startContainer;
16239 
16240 				if (startContainer.nodeType == 3) {
16241 					startOffset = 0;
16242 				}
16243 			}
16244 
16245 			if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
16246 				endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
16247 				endContainer = endContainer.previousSibling || endContainer;
16248 
16249 				if (endContainer.nodeType == 3) {
16250 					endOffset = endContainer.length;
16251 				}
16252 			}
16253 
16254 			if (format[0].inline) {
16255 				if (rng.collapsed) {
16256 					// Expand left to closest word boundary
16257 					endPoint = findWordEndPoint(startContainer, startOffset, true);
16258 					if (endPoint) {
16259 						startContainer = endPoint.container;
16260 						startOffset = endPoint.offset;
16261 					}
16262 
16263 					// Expand right to closest word boundary
16264 					endPoint = findWordEndPoint(endContainer, endOffset);
16265 					if (endPoint) {
16266 						endContainer = endPoint.container;
16267 						endOffset = endPoint.offset;
16268 					}
16269 				}
16270 
16271 				// Avoid applying formatting to a trailing space.
16272 				leaf = findLeaf(endContainer, endOffset);
16273 				if (leaf.node) {
16274 					while (leaf.node && leaf.offset === 0 && leaf.node.previousSibling) {
16275 						leaf = findLeaf(leaf.node.previousSibling);
16276 					}
16277 
16278 					if (leaf.node && leaf.offset > 0 && leaf.node.nodeType === 3 &&
16279 							leaf.node.nodeValue.charAt(leaf.offset - 1) === ' ') {
16280 
16281 						if (leaf.offset > 1) {
16282 							endContainer = leaf.node;
16283 							endContainer.splitText(leaf.offset - 1);
16284 						}
16285 					}
16286 				}
16287 			}
16288 
16289 			// Move start/end point up the tree if the leaves are sharp and if we are in different containers
16290 			// Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!
16291 			// This will reduce the number of wrapper elements that needs to be created
16292 			// Move start point up the tree
16293 			if (format[0].inline || format[0].block_expand) {
16294 				if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {
16295 					startContainer = findParentContainer(true);
16296 				}
16297 
16298 				if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {
16299 					endContainer = findParentContainer();
16300 				}
16301 			}
16302 
16303 			// Expand start/end container to matching selector
16304 			if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {
16305 				// Find new startContainer/endContainer if there is better one
16306 				startContainer = findSelectorEndPoint(startContainer, 'previousSibling');
16307 				endContainer = findSelectorEndPoint(endContainer, 'nextSibling');
16308 			}
16309 
16310 			// Expand start/end container to matching block element or text node
16311 			if (format[0].block || format[0].selector) {
16312 				// Find new startContainer/endContainer if there is better one
16313 				startContainer = findBlockEndPoint(startContainer, 'previousSibling');
16314 				endContainer = findBlockEndPoint(endContainer, 'nextSibling');
16315 
16316 				// Non block element then try to expand up the leaf
16317 				if (format[0].block) {
16318 					if (!isBlock(startContainer)) {
16319 						startContainer = findParentContainer(true);
16320 					}
16321 
16322 					if (!isBlock(endContainer)) {
16323 						endContainer = findParentContainer();
16324 					}
16325 				}
16326 			}
16327 
16328 			// Setup index for startContainer
16329 			if (startContainer.nodeType == 1) {
16330 				startOffset = nodeIndex(startContainer);
16331 				startContainer = startContainer.parentNode;
16332 			}
16333 
16334 			// Setup index for endContainer
16335 			if (endContainer.nodeType == 1) {
16336 				endOffset = nodeIndex(endContainer) + 1;
16337 				endContainer = endContainer.parentNode;
16338 			}
16339 
16340 			// Return new range like object
16341 			return {
16342 				startContainer: startContainer,
16343 				startOffset: startOffset,
16344 				endContainer: endContainer,
16345 				endOffset: endOffset
16346 			};
16347 		}
16348 
16349 		/**
16350 		 * Removes the specified format for the specified node. It will also remove the node if it doesn't have
16351 		 * any attributes if the format specifies it to do so.
16352 		 *
16353 		 * @private
16354 		 * @param {Object} format Format object with items to remove from node.
16355 		 * @param {Object} vars Name/value object with variables to apply to format.
16356 		 * @param {Node} node Node to remove the format styles on.
16357 		 * @param {Node} compare_node Optional compare node, if specified the styles will be compared to that node.
16358 		 * @return {Boolean} True/false if the node was removed or not.
16359 		 */
16360 		function removeFormat(format, vars, node, compare_node) {
16361 			var i, attrs, stylesModified;
16362 
16363 			// Check if node matches format
16364 			if (!matchName(node, format)) {
16365 				return FALSE;
16366 			}
16367 
16368 			// Should we compare with format attribs and styles
16369 			if (format.remove != 'all') {
16370 				// Remove styles
16371 				each(format.styles, function(value, name) {
16372 					value = normalizeStyleValue(replaceVars(value, vars), name);
16373 
16374 					// Indexed array
16375 					if (typeof(name) === 'number') {
16376 						name = value;
16377 						compare_node = 0;
16378 					}
16379 
16380 					if (!compare_node || isEq(getStyle(compare_node, name), value)) {
16381 						dom.setStyle(node, name, '');
16382 					}
16383 
16384 					stylesModified = 1;
16385 				});
16386 
16387 				// Remove style attribute if it's empty
16388 				if (stylesModified && dom.getAttrib(node, 'style') === '') {
16389 					node.removeAttribute('style');
16390 					node.removeAttribute('data-mce-style');
16391 				}
16392 
16393 				// Remove attributes
16394 				each(format.attributes, function(value, name) {
16395 					var valueOut;
16396 
16397 					value = replaceVars(value, vars);
16398 
16399 					// Indexed array
16400 					if (typeof(name) === 'number') {
16401 						name = value;
16402 						compare_node = 0;
16403 					}
16404 
16405 					if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) {
16406 						// Keep internal classes
16407 						if (name == 'class') {
16408 							value = dom.getAttrib(node, name);
16409 							if (value) {
16410 								// Build new class value where everything is removed except the internal prefixed classes
16411 								valueOut = '';
16412 								each(value.split(/\s+/), function(cls) {
16413 									if (/mce\w+/.test(cls)) {
16414 										valueOut += (valueOut ? ' ' : '') + cls;
16415 									}
16416 								});
16417 
16418 								// We got some internal classes left
16419 								if (valueOut) {
16420 									dom.setAttrib(node, name, valueOut);
16421 									return;
16422 								}
16423 							}
16424 						}
16425 
16426 						// IE6 has a bug where the attribute doesn't get removed correctly
16427 						if (name == "class") {
16428 							node.removeAttribute('className');
16429 						}
16430 
16431 						// Remove mce prefixed attributes
16432 						if (MCE_ATTR_RE.test(name)) {
16433 							node.removeAttribute('data-mce-' + name);
16434 						}
16435 
16436 						node.removeAttribute(name);
16437 					}
16438 				});
16439 
16440 				// Remove classes
16441 				each(format.classes, function(value) {
16442 					value = replaceVars(value, vars);
16443 
16444 					if (!compare_node || dom.hasClass(compare_node, value)) {
16445 						dom.removeClass(node, value);
16446 					}
16447 				});
16448 
16449 				// Check for non internal attributes
16450 				attrs = dom.getAttribs(node);
16451 				for (i = 0; i < attrs.length; i++) {
16452 					if (attrs[i].nodeName.indexOf('_') !== 0) {
16453 						return FALSE;
16454 					}
16455 				}
16456 			}
16457 
16458 			// Remove the inline child if it's empty for example <b> or <span>
16459 			if (format.remove != 'none') {
16460 				removeNode(node, format);
16461 				return TRUE;
16462 			}
16463 		}
16464 
16465 		/**
16466 		 * Removes the node and wrap it's children in paragraphs before doing so or
16467 		 * appends BR elements to the beginning/end of the block element if forcedRootBlocks is disabled.
16468 		 *
16469 		 * If the div in the node below gets removed:
16470 		 *  text<div>text</div>text
16471 		 *
16472 		 * Output becomes:
16473 		 *  text<div><br />text<br /></div>text
16474 		 *
16475 		 * So when the div is removed the result is:
16476 		 *  text<br />text<br />text
16477 		 *
16478 		 * @private
16479 		 * @param {Node} node Node to remove + apply BR/P elements to.
16480 		 * @param {Object} format Format rule.
16481 		 * @return {Node} Input node.
16482 		 */
16483 		function removeNode(node, format) {
16484 			var parentNode = node.parentNode, rootBlockElm;
16485 
16486 			function find(node, next, inc) {
16487 				node = getNonWhiteSpaceSibling(node, next, inc);
16488 
16489 				return !node || (node.nodeName == 'BR' || isBlock(node));
16490 			}
16491 
16492 			if (format.block) {
16493 				if (!forcedRootBlock) {
16494 					// Append BR elements if needed before we remove the block
16495 					if (isBlock(node) && !isBlock(parentNode)) {
16496 						if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1)) {
16497 							node.insertBefore(dom.create('br'), node.firstChild);
16498 						}
16499 
16500 						if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1)) {
16501 							node.appendChild(dom.create('br'));
16502 						}
16503 					}
16504 				} else {
16505 					// Wrap the block in a forcedRootBlock if we are at the root of document
16506 					if (parentNode == dom.getRoot()) {
16507 						if (!format.list_block || !isEq(node, format.list_block)) {
16508 							each(grep(node.childNodes), function(node) {
16509 								if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) {
16510 									if (!rootBlockElm) {
16511 										rootBlockElm = wrap(node, forcedRootBlock);
16512 										dom.setAttribs(rootBlockElm, ed.settings.forced_root_block_attrs);
16513 									} else {
16514 										rootBlockElm.appendChild(node);
16515 									}
16516 								} else {
16517 									rootBlockElm = 0;
16518 								}
16519 							});
16520 						}
16521 					}
16522 				}
16523 			}
16524 
16525 			// Never remove nodes that isn't the specified inline element if a selector is specified too
16526 			if (format.selector && format.inline && !isEq(format.inline, node)) {
16527 				return;
16528 			}
16529 
16530 			dom.remove(node, 1);
16531 		}
16532 
16533 		/**
16534 		 * Returns the next/previous non whitespace node.
16535 		 *
16536 		 * @private
16537 		 * @param {Node} node Node to start at.
16538 		 * @param {boolean} next (Optional) Include next or previous node defaults to previous.
16539 		 * @param {boolean} inc (Optional) Include the current node in checking. Defaults to false.
16540 		 * @return {Node} Next or previous node or undefined if it wasn't found.
16541 		 */
16542 		function getNonWhiteSpaceSibling(node, next, inc) {
16543 			if (node) {
16544 				next = next ? 'nextSibling' : 'previousSibling';
16545 
16546 				for (node = inc ? node : node[next]; node; node = node[next]) {
16547 					if (node.nodeType == 1 || !isWhiteSpaceNode(node)) {
16548 						return node;
16549 					}
16550 				}
16551 			}
16552 		}
16553 
16554 		/**
16555 		 * Checks if the specified node is a bookmark node or not.
16556 		 *
16557 		 * @private
16558 		 * @param {Node} node Node to check if it's a bookmark node or not.
16559 		 * @return {Boolean} true/false if the node is a bookmark node.
16560 		 */
16561 		function isBookmarkNode(node) {
16562 			return node && node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark';
16563 		}
16564 
16565 		/**
16566 		 * Merges the next/previous sibling element if they match.
16567 		 *
16568 		 * @private
16569 		 * @param {Node} prev Previous node to compare/merge.
16570 		 * @param {Node} next Next node to compare/merge.
16571 		 * @return {Node} Next node if we didn't merge and prev node if we did.
16572 		 */
16573 		function mergeSiblings(prev, next) {
16574 			var sibling, tmpSibling;
16575 
16576 			/**
16577 			 * Compares two nodes and checks if it's attributes and styles matches.
16578 			 * This doesn't compare classes as items since their order is significant.
16579 			 *
16580 			 * @private
16581 			 * @param {Node} node1 First node to compare with.
16582 			 * @param {Node} node2 Second node to compare with.
16583 			 * @return {boolean} True/false if the nodes are the same or not.
16584 			 */
16585 			function compareElements(node1, node2) {
16586 				// Not the same name
16587 				if (node1.nodeName != node2.nodeName) {
16588 					return FALSE;
16589 				}
16590 
16591 				/**
16592 				 * Returns all the nodes attributes excluding internal ones, styles and classes.
16593 				 *
16594 				 * @private
16595 				 * @param {Node} node Node to get attributes from.
16596 				 * @return {Object} Name/value object with attributes and attribute values.
16597 				 */
16598 				function getAttribs(node) {
16599 					var attribs = {};
16600 
16601 					each(dom.getAttribs(node), function(attr) {
16602 						var name = attr.nodeName.toLowerCase();
16603 
16604 						// Don't compare internal attributes or style
16605 						if (name.indexOf('_') !== 0 && name !== 'style' && name !== 'data-mce-style') {
16606 							attribs[name] = dom.getAttrib(node, name);
16607 						}
16608 					});
16609 
16610 					return attribs;
16611 				}
16612 
16613 				/**
16614 				 * Compares two objects checks if it's key + value exists in the other one.
16615 				 *
16616 				 * @private
16617 				 * @param {Object} obj1 First object to compare.
16618 				 * @param {Object} obj2 Second object to compare.
16619 				 * @return {boolean} True/false if the objects matches or not.
16620 				 */
16621 				function compareObjects(obj1, obj2) {
16622 					var value, name;
16623 
16624 					for (name in obj1) {
16625 						// Obj1 has item obj2 doesn't have
16626 						if (obj1.hasOwnProperty(name)) {
16627 							value = obj2[name];
16628 
16629 							// Obj2 doesn't have obj1 item
16630 							if (value === undef) {
16631 								return FALSE;
16632 							}
16633 
16634 							// Obj2 item has a different value
16635 							if (obj1[name] != value) {
16636 								return FALSE;
16637 							}
16638 
16639 							// Delete similar value
16640 							delete obj2[name];
16641 						}
16642 					}
16643 
16644 					// Check if obj 2 has something obj 1 doesn't have
16645 					for (name in obj2) {
16646 						// Obj2 has item obj1 doesn't have
16647 						if (obj2.hasOwnProperty(name)) {
16648 							return FALSE;
16649 						}
16650 					}
16651 
16652 					return TRUE;
16653 				}
16654 
16655 				// Attribs are not the same
16656 				if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
16657 					return FALSE;
16658 				}
16659 
16660 				// Styles are not the same
16661 				if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
16662 					return FALSE;
16663 				}
16664 
16665 				return !isBookmarkNode(node1) && !isBookmarkNode(node2);
16666 			}
16667 
16668 			function findElementSibling(node, sibling_name) {
16669 				for (sibling = node; sibling; sibling = sibling[sibling_name]) {
16670 					if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0) {
16671 						return node;
16672 					}
16673 
16674 					if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) {
16675 						return sibling;
16676 					}
16677 				}
16678 
16679 				return node;
16680 			}
16681 
16682 			// Check if next/prev exists and that they are elements
16683 			if (prev && next) {
16684 				// If previous sibling is empty then jump over it
16685 				prev = findElementSibling(prev, 'previousSibling');
16686 				next = findElementSibling(next, 'nextSibling');
16687 
16688 				// Compare next and previous nodes
16689 				if (compareElements(prev, next)) {
16690 					// Append nodes between
16691 					for (sibling = prev.nextSibling; sibling && sibling != next;) {
16692 						tmpSibling = sibling;
16693 						sibling = sibling.nextSibling;
16694 						prev.appendChild(tmpSibling);
16695 					}
16696 
16697 					// Remove next node
16698 					dom.remove(next);
16699 
16700 					// Move children into prev node
16701 					each(grep(next.childNodes), function(node) {
16702 						prev.appendChild(node);
16703 					});
16704 
16705 					return prev;
16706 				}
16707 			}
16708 
16709 			return next;
16710 		}
16711 
16712 		function getContainer(rng, start) {
16713 			var container, offset, lastIdx;
16714 
16715 			container = rng[start ? 'startContainer' : 'endContainer'];
16716 			offset = rng[start ? 'startOffset' : 'endOffset'];
16717 
16718 			if (container.nodeType == 1) {
16719 				lastIdx = container.childNodes.length - 1;
16720 
16721 				if (!start && offset) {
16722 					offset--;
16723 				}
16724 
16725 				container = container.childNodes[offset > lastIdx ? lastIdx : offset];
16726 			}
16727 
16728 			// If start text node is excluded then walk to the next node
16729 			if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {
16730 				container = new TreeWalker(container, ed.getBody()).next() || container;
16731 			}
16732 
16733 			// If end text node is excluded then walk to the previous node
16734 			if (container.nodeType === 3 && !start && offset === 0) {
16735 				container = new TreeWalker(container, ed.getBody()).prev() || container;
16736 			}
16737 
16738 			return container;
16739 		}
16740 
16741 		function performCaretAction(type, name, vars) {
16742 			var caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
16743 
16744 			// Creates a caret container bogus element
16745 			function createCaretContainer(fill) {
16746 				var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});
16747 
16748 				if (fill) {
16749 					caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR));
16750 				}
16751 
16752 				return caretContainer;
16753 			}
16754 
16755 			function isCaretContainerEmpty(node, nodes) {
16756 				while (node) {
16757 					if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) {
16758 						return false;
16759 					}
16760 
16761 					// Collect nodes
16762 					if (nodes && node.nodeType === 1) {
16763 						nodes.push(node);
16764 					}
16765 
16766 					node = node.firstChild;
16767 				}
16768 
16769 				return true;
16770 			}
16771 
16772 			// Returns any parent caret container element
16773 			function getParentCaretContainer(node) {
16774 				while (node) {
16775 					if (node.id === caretContainerId) {
16776 						return node;
16777 					}
16778 
16779 					node = node.parentNode;
16780 				}
16781 			}
16782 
16783 			// Finds the first text node in the specified node
16784 			function findFirstTextNode(node) {
16785 				var walker;
16786 
16787 				if (node) {
16788 					walker = new TreeWalker(node, node);
16789 
16790 					for (node = walker.current(); node; node = walker.next()) {
16791 						if (node.nodeType === 3) {
16792 							return node;
16793 						}
16794 					}
16795 				}
16796 			}
16797 
16798 			// Removes the caret container for the specified node or all on the current document
16799 			function removeCaretContainer(node, move_caret) {
16800 				var child, rng;
16801 
16802 				if (!node) {
16803 					node = getParentCaretContainer(selection.getStart());
16804 
16805 					if (!node) {
16806 						while ((node = dom.get(caretContainerId))) {
16807 							removeCaretContainer(node, false);
16808 						}
16809 					}
16810 				} else {
16811 					rng = selection.getRng(true);
16812 
16813 					if (isCaretContainerEmpty(node)) {
16814 						if (move_caret !== false) {
16815 							rng.setStartBefore(node);
16816 							rng.setEndBefore(node);
16817 						}
16818 
16819 						dom.remove(node);
16820 					} else {
16821 						child = findFirstTextNode(node);
16822 
16823 						if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
16824 							child = child.deleteData(0, 1);
16825 						}
16826 
16827 						dom.remove(node, 1);
16828 					}
16829 
16830 					selection.setRng(rng);
16831 				}
16832 			}
16833 
16834 			// Applies formatting to the caret postion
16835 			function applyCaretFormat() {
16836 				var rng, caretContainer, textNode, offset, bookmark, container, text;
16837 
16838 				rng = selection.getRng(true);
16839 				offset = rng.startOffset;
16840 				container = rng.startContainer;
16841 				text = container.nodeValue;
16842 
16843 				caretContainer = getParentCaretContainer(selection.getStart());
16844 				if (caretContainer) {
16845 					textNode = findFirstTextNode(caretContainer);
16846 				}
16847 
16848 				// Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character
16849 				if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {
16850 					// Get bookmark of caret position
16851 					bookmark = selection.getBookmark();
16852 
16853 					// Collapse bookmark range (WebKit)
16854 					rng.collapse(true);
16855 
16856 					// Expand the range to the closest word and split it at those points
16857 					rng = expandRng(rng, get(name));
16858 					rng = rangeUtils.split(rng);
16859 
16860 					// Apply the format to the range
16861 					apply(name, vars, rng);
16862 
16863 					// Move selection back to caret position
16864 					selection.moveToBookmark(bookmark);
16865 				} else {
16866 					if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) {
16867 						caretContainer = createCaretContainer(true);
16868 						textNode = caretContainer.firstChild;
16869 
16870 						rng.insertNode(caretContainer);
16871 						offset = 1;
16872 
16873 						apply(name, vars, caretContainer);
16874 					} else {
16875 						apply(name, vars, caretContainer);
16876 					}
16877 
16878 					// Move selection to text node
16879 					selection.setCursorLocation(textNode, offset);
16880 				}
16881 			}
16882 
16883 			function removeCaretFormat() {
16884 				var rng = selection.getRng(true), container, offset, bookmark,
16885 					hasContentAfter, node, formatNode, parents = [], i, caretContainer;
16886 
16887 				container = rng.startContainer;
16888 				offset = rng.startOffset;
16889 				node = container;
16890 
16891 				if (container.nodeType == 3) {
16892 					if (offset != container.nodeValue.length || container.nodeValue === INVISIBLE_CHAR) {
16893 						hasContentAfter = true;
16894 					}
16895 
16896 					node = node.parentNode;
16897 				}
16898 
16899 				while (node) {
16900 					if (matchNode(node, name, vars)) {
16901 						formatNode = node;
16902 						break;
16903 					}
16904 
16905 					if (node.nextSibling) {
16906 						hasContentAfter = true;
16907 					}
16908 
16909 					parents.push(node);
16910 					node = node.parentNode;
16911 				}
16912 
16913 				// Node doesn't have the specified format
16914 				if (!formatNode) {
16915 					return;
16916 				}
16917 
16918 				// Is there contents after the caret then remove the format on the element
16919 				if (hasContentAfter) {
16920 					// Get bookmark of caret position
16921 					bookmark = selection.getBookmark();
16922 
16923 					// Collapse bookmark range (WebKit)
16924 					rng.collapse(true);
16925 
16926 					// Expand the range to the closest word and split it at those points
16927 					rng = expandRng(rng, get(name), true);
16928 					rng = rangeUtils.split(rng);
16929 
16930 					// Remove the format from the range
16931 					remove(name, vars, rng);
16932 
16933 					// Move selection back to caret position
16934 					selection.moveToBookmark(bookmark);
16935 				} else {
16936 					caretContainer = createCaretContainer();
16937 
16938 					node = caretContainer;
16939 					for (i = parents.length - 1; i >= 0; i--) {
16940 						node.appendChild(dom.clone(parents[i], false));
16941 						node = node.firstChild;
16942 					}
16943 
16944 					// Insert invisible character into inner most format element
16945 					node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR));
16946 					node = node.firstChild;
16947 
16948 					var block = dom.getParent(formatNode, isTextBlock);
16949 
16950 					if (block && dom.isEmpty(block)) {
16951 						// Replace formatNode with caretContainer when removing format from empty block like <p><b>|</b></p>
16952 						formatNode.parentNode.replaceChild(caretContainer, formatNode);
16953 					} else {
16954 						// Insert caret container after the formated node
16955 						dom.insertAfter(caretContainer, formatNode);
16956 					}
16957 
16958 					// Move selection to text node
16959 					selection.setCursorLocation(node, 1);
16960 
16961 					// If the formatNode is empty, we can remove it safely. 
16962 					if (dom.isEmpty(formatNode)) {
16963 						dom.remove(formatNode);
16964 					}
16965 				}
16966 			}
16967 
16968 			// Checks if the parent caret container node isn't empty if that is the case it
16969 			// will remove the bogus state on all children that isn't empty
16970 			function unmarkBogusCaretParents() {
16971 				var caretContainer;
16972 
16973 				caretContainer = getParentCaretContainer(selection.getStart());
16974 				if (caretContainer && !dom.isEmpty(caretContainer)) {
16975 					walk(caretContainer, function(node) {
16976 						if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {
16977 							dom.setAttrib(node, 'data-mce-bogus', null);
16978 						}
16979 					}, 'childNodes');
16980 				}
16981 			}
16982 
16983 			// Only bind the caret events once
16984 			if (!ed._hasCaretEvents) {
16985 				// Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
16986 				markCaretContainersBogus = function() {
16987 					var nodes = [], i;
16988 
16989 					if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
16990 						// Mark children
16991 						i = nodes.length;
16992 						while (i--) {
16993 							dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
16994 						}
16995 					}
16996 				};
16997 
16998 				disableCaretContainer = function(e) {
16999 					var keyCode = e.keyCode;
17000 
17001 					removeCaretContainer();
17002 
17003 					// Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
17004 					if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
17005 						removeCaretContainer(getParentCaretContainer(selection.getStart()));
17006 					}
17007 
17008 					unmarkBogusCaretParents();
17009 				};
17010 
17011 				// Remove bogus state if they got filled by contents using editor.selection.setContent
17012 				ed.on('SetContent', function(e) {
17013 					if (e.selection) {
17014 						unmarkBogusCaretParents();
17015 					}
17016 				});
17017 				ed._hasCaretEvents = true;
17018 			}
17019 
17020 			// Do apply or remove caret format
17021 			if (type == "apply") {
17022 				applyCaretFormat();
17023 			} else {
17024 				removeCaretFormat();
17025 			}
17026 		}
17027 
17028 		/**
17029 		 * Moves the start to the first suitable text node.
17030 		 */
17031 		function moveStart(rng) {
17032 			var container = rng.startContainer,
17033 					offset = rng.startOffset, isAtEndOfText,
17034 					walker, node, nodes, tmpNode;
17035 
17036 			// Convert text node into index if possible
17037 			if (container.nodeType == 3 && offset >= container.nodeValue.length) {
17038 				// Get the parent container location and walk from there
17039 				offset = nodeIndex(container);
17040 				container = container.parentNode;
17041 				isAtEndOfText = true;
17042 			}
17043 
17044 			// Move startContainer/startOffset in to a suitable node
17045 			if (container.nodeType == 1) {
17046 				nodes = container.childNodes;
17047 				container = nodes[Math.min(offset, nodes.length - 1)];
17048 				walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));
17049 
17050 				// If offset is at end of the parent node walk to the next one
17051 				if (offset > nodes.length - 1 || isAtEndOfText) {
17052 					walker.next();
17053 				}
17054 
17055 				for (node = walker.current(); node; node = walker.next()) {
17056 					if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
17057 						// IE has a "neat" feature where it moves the start node into the closest element
17058 						// we can avoid this by inserting an element before it and then remove it after we set the selection
17059 						tmpNode = dom.create('a', null, INVISIBLE_CHAR);
17060 						node.parentNode.insertBefore(tmpNode, node);
17061 
17062 						// Set selection and remove tmpNode
17063 						rng.setStart(node, 0);
17064 						selection.setRng(rng);
17065 						dom.remove(tmpNode);
17066 
17067 						return;
17068 					}
17069 				}
17070 			}
17071 		}
17072 	};
17073 });
17074 
17075 // Included from: js/tinymce/classes/UndoManager.js
17076 
17077 /**
17078  * UndoManager.js
17079  *
17080  * Copyright, Moxiecode Systems AB
17081  * Released under LGPL License.
17082  *
17083  * License: http://www.tinymce.com/license
17084  * Contributing: http://www.tinymce.com/contributing
17085  */
17086 
17087 /**
17088  * This class handles the undo/redo history levels for the editor. Since the build in undo/redo has major drawbacks a custom one was needed.
17089  *
17090  * @class tinymce.UndoManager
17091  */
17092 define("tinymce/UndoManager", [
17093 	"tinymce/Env",
17094 	"tinymce/util/Tools"
17095 ], function(Env, Tools) {
17096 	var trim = Tools.trim, trimContentRegExp;
17097 
17098 	trimContentRegExp = new RegExp([
17099 		'<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\\/span>', // Trim bogus spans like caret containers
17100 		'<div[^>]+data-mce-bogus[^>]+><\\/div>', // Trim bogus divs like resize handles
17101 		'\\s?data-mce-selected="[^"]+"' // Trim temporaty data-mce prefixed attributes like data-mce-selected
17102 	].join('|'), 'gi');
17103 
17104 	return function(editor) {
17105 		var self = this, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, locks = 0;
17106 
17107 		// Returns a trimmed version of the current editor contents
17108 		function getContent() {
17109 			return trim(editor.getContent({format: 'raw', no_events: 1}).replace(trimContentRegExp, ''));
17110 		}
17111 
17112 		function addNonTypingUndoLevel(e) {
17113 			self.typing = false;
17114 			self.add({}, e);
17115 		}
17116 
17117 		// Add initial undo level when the editor is initialized
17118 		editor.on('init', function() {
17119 			self.add();
17120 		});
17121 
17122 		// Get position before an execCommand is processed
17123 		editor.on('BeforeExecCommand', function(e) {
17124 			var cmd = e.command;
17125 
17126 			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
17127 				self.beforeChange();
17128 			}
17129 		});
17130 
17131 		// Add undo level after an execCommand call was made
17132 		editor.on('ExecCommand', function(e) {
17133 			var cmd = e.command;
17134 
17135 			if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint') {
17136 				addNonTypingUndoLevel(e);
17137 			}
17138 		});
17139 
17140 		editor.on('ObjectResizeStart', function() {
17141 			self.beforeChange();
17142 		});
17143 
17144 		editor.on('SaveContent ObjectResized blur', addNonTypingUndoLevel);
17145 		editor.on('DragEnd', addNonTypingUndoLevel);
17146 
17147 		editor.on('KeyUp', function(e) {
17148 			var keyCode = e.keyCode;
17149 
17150 			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
17151 				addNonTypingUndoLevel();
17152 				editor.nodeChanged();
17153 			}
17154 
17155 			if (keyCode == 46 || keyCode == 8 || (Env.mac && (keyCode == 91 || keyCode == 93))) {
17156 				editor.nodeChanged();
17157 			}
17158 
17159 			// Fire a TypingUndo event on the first character entered
17160 			if (isFirstTypedCharacter && self.typing) {
17161 				// Make the it dirty if the content was changed after typing the first character
17162 				if (!editor.isDirty()) {
17163 					editor.isNotDirty = !data[0] || getContent() == data[0].content;
17164 
17165 					// Fire initial change event
17166 					if (!editor.isNotDirty) {
17167 						editor.fire('change', {level: data[0], lastLevel: null});
17168 					}
17169 				}
17170 
17171 				editor.fire('TypingUndo');
17172 				isFirstTypedCharacter = false;
17173 				editor.nodeChanged();
17174 			}
17175 		});
17176 
17177 		editor.on('KeyDown', function(e) {
17178 			var keyCode = e.keyCode;
17179 
17180 			// Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
17181 			if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
17182 				if (self.typing) {
17183 					addNonTypingUndoLevel(e);
17184 				}
17185 
17186 				return;
17187 			}
17188 
17189 			// If key isn't shift,ctrl,alt,capslock,metakey
17190 			if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing) {
17191 				self.beforeChange();
17192 				self.typing = true;
17193 				self.add({}, e);
17194 				isFirstTypedCharacter = true;
17195 			}
17196 		});
17197 
17198 		editor.on('MouseDown', function(e) {
17199 			if (self.typing) {
17200 				addNonTypingUndoLevel(e);
17201 			}
17202 		});
17203 
17204 		// Add keyboard shortcuts for undo/redo keys
17205 		editor.addShortcut('ctrl+z', '', 'Undo');
17206 		editor.addShortcut('ctrl+y,ctrl+shift+z', '', 'Redo');
17207 
17208 		editor.on('AddUndo Undo Redo ClearUndos MouseUp', function(e) {
17209 			if (!e.isDefaultPrevented()) {
17210 				editor.nodeChanged();
17211 			}
17212 		});
17213 
17214 		self = {
17215 			// Explose for debugging reasons
17216 			data: data,
17217 
17218 			/**
17219 			 * State if the user is currently typing or not. This will add a typing operation into one undo
17220 			 * level instead of one new level for each keystroke.
17221 			 *
17222 			 * @field {Boolean} typing
17223 			 */
17224 			typing: false,
17225 
17226 			/**
17227 			 * Stores away a bookmark to be used when performing an undo action so that the selection is before
17228 			 * the change has been made.
17229 			 *
17230 			 * @method beforeChange
17231 			 */
17232 			beforeChange: function() {
17233 				if (!locks) {
17234 					beforeBookmark = editor.selection.getBookmark(2, true);
17235 				}
17236 			},
17237 
17238 			/**
17239 			 * Adds a new undo level/snapshot to the undo list.
17240 			 *
17241 			 * @method add
17242 			 * @param {Object} level Optional undo level object to add.
17243 			 * @param {DOMEvent} Event Optional event responsible for the creation of the undo level.
17244 			 * @return {Object} Undo level that got added or null it a level wasn't needed.
17245 			 */
17246 			add: function(level, event) {
17247 				var i, settings = editor.settings, lastLevel;
17248 
17249 				level = level || {};
17250 				level.content = getContent();
17251 
17252 				if (locks || editor.removed) {
17253 					return null;
17254 				}
17255 
17256 				lastLevel = data[index];
17257 				if (editor.fire('BeforeAddUndo', {level: level, lastLevel: lastLevel, originalEvent: event}).isDefaultPrevented()) {
17258 					return null;
17259 				}
17260 
17261 				// Add undo level if needed
17262 				if (lastLevel && lastLevel.content == level.content) {
17263 					return null;
17264 				}
17265 
17266 				// Set before bookmark on previous level
17267 				if (data[index]) {
17268 					data[index].beforeBookmark = beforeBookmark;
17269 				}
17270 
17271 				// Time to compress
17272 				if (settings.custom_undo_redo_levels) {
17273 					if (data.length > settings.custom_undo_redo_levels) {
17274 						for (i = 0; i < data.length - 1; i++) {
17275 							data[i] = data[i + 1];
17276 						}
17277 
17278 						data.length--;
17279 						index = data.length;
17280 					}
17281 				}
17282 
17283 				// Get a non intrusive normalized bookmark
17284 				level.bookmark = editor.selection.getBookmark(2, true);
17285 
17286 				// Crop array if needed
17287 				if (index < data.length - 1) {
17288 					data.length = index + 1;
17289 				}
17290 
17291 				data.push(level);
17292 				index = data.length - 1;
17293 
17294 				var args = {level: level, lastLevel: lastLevel, originalEvent: event};
17295 
17296 				editor.fire('AddUndo', args);
17297 
17298 				if (index > 0) {
17299 					editor.isNotDirty = false;
17300 					editor.fire('change', args);
17301 				}
17302 
17303 				return level;
17304 			},
17305 
17306 			/**
17307 			 * Undoes the last action.
17308 			 *
17309 			 * @method undo
17310 			 * @return {Object} Undo level or null if no undo was performed.
17311 			 */
17312 			undo: function() {
17313 				var level;
17314 
17315 				if (self.typing) {
17316 					self.add();
17317 					self.typing = false;
17318 				}
17319 
17320 				if (index > 0) {
17321 					level = data[--index];
17322 
17323 					// Undo to first index then set dirty state to false
17324 					if (index === 0) {
17325 						editor.isNotDirty = true;
17326 					}
17327 
17328 					editor.setContent(level.content, {format: 'raw'});
17329 					editor.selection.moveToBookmark(level.beforeBookmark);
17330 
17331 					editor.fire('undo', {level: level});
17332 				}
17333 
17334 				return level;
17335 			},
17336 
17337 			/**
17338 			 * Redoes the last action.
17339 			 *
17340 			 * @method redo
17341 			 * @return {Object} Redo level or null if no redo was performed.
17342 			 */
17343 			redo: function() {
17344 				var level;
17345 
17346 				if (index < data.length - 1) {
17347 					level = data[++index];
17348 
17349 					editor.setContent(level.content, {format: 'raw'});
17350 					editor.selection.moveToBookmark(level.bookmark);
17351 
17352 					editor.fire('redo', {level: level});
17353 				}
17354 
17355 				return level;
17356 			},
17357 
17358 			/**
17359 			 * Removes all undo levels.
17360 			 *
17361 			 * @method clear
17362 			 */
17363 			clear: function() {
17364 				data = [];
17365 				index = 0;
17366 				self.typing = false;
17367 				editor.fire('ClearUndos');
17368 			},
17369 
17370 			/**
17371 			 * Returns true/false if the undo manager has any undo levels.
17372 			 *
17373 			 * @method hasUndo
17374 			 * @return {Boolean} true/false if the undo manager has any undo levels.
17375 			 */
17376 			hasUndo: function() {
17377 				// Has undo levels or typing and content isn't the same as the initial level
17378 				return index > 0 || (self.typing && data[0] && getContent() != data[0].content);
17379 			},
17380 
17381 			/**
17382 			 * Returns true/false if the undo manager has any redo levels.
17383 			 *
17384 			 * @method hasRedo
17385 			 * @return {Boolean} true/false if the undo manager has any redo levels.
17386 			 */
17387 			hasRedo: function() {
17388 				return index < data.length - 1 && !this.typing;
17389 			},
17390 
17391 			/**
17392 			 * Executes the specified function in an undo transation. The selection
17393 			 * before the modification will be stored to the undo stack and if the DOM changes
17394 			 * it will add a new undo level. Any methods within the transation that adds undo levels will
17395 			 * be ignored. So a transation can include calls to execCommand or editor.insertContent.
17396 			 *
17397 			 * @method transact
17398 			 * @param {function} callback Function to execute dom manipulation logic in.
17399 			 */
17400 			transact: function(callback) {
17401 				self.beforeChange();
17402 
17403 				try {
17404 					locks++;
17405 					callback();
17406 				} finally {
17407 					locks--;
17408 				}
17409 
17410 				self.add();
17411 			}
17412 		};
17413 
17414 		return self;
17415 	};
17416 });
17417 
17418 // Included from: js/tinymce/classes/EnterKey.js
17419 
17420 /**
17421  * EnterKey.js
17422  *
17423  * Copyright, Moxiecode Systems AB
17424  * Released under LGPL License.
17425  *
17426  * License: http://www.tinymce.com/license
17427  * Contributing: http://www.tinymce.com/contributing
17428  */
17429 
17430 /**
17431  * Contains logic for handling the enter key to split/generate block elements.
17432  */
17433 define("tinymce/EnterKey", [
17434 	"tinymce/dom/TreeWalker",
17435 	"tinymce/dom/RangeUtils",
17436 	"tinymce/Env"
17437 ], function(TreeWalker, RangeUtils, Env) {
17438 	var isIE = Env.ie && Env.ie < 11;
17439 
17440 	return function(editor) {
17441 		var dom = editor.dom, selection = editor.selection, settings = editor.settings;
17442 		var undoManager = editor.undoManager, schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements();
17443 
17444 		function handleEnterKey(evt) {
17445 			var rng, tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,
17446 				newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
17447 
17448 			// Returns true if the block can be split into two blocks or not
17449 			function canSplitBlock(node) {
17450 				return node &&
17451 					dom.isBlock(node) &&
17452 					!/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
17453 					!/^(fixed|absolute)/i.test(node.style.position) &&
17454 					dom.getContentEditable(node) !== "true";
17455 			}
17456 
17457 			// Renders empty block on IE
17458 			function renderBlockOnIE(block) {
17459 				var oldRng;
17460 
17461 				if (dom.isBlock(block)) {
17462 					oldRng = selection.getRng();
17463 					block.appendChild(dom.create('span', null, '\u00a0'));
17464 					selection.select(block);
17465 					block.lastChild.outerHTML = '';
17466 					selection.setRng(oldRng);
17467 				}
17468 			}
17469 
17470 			// Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>
17471 			function trimInlineElementsOnLeftSideOfBlock(block) {
17472 				var node = block, firstChilds = [], i;
17473 
17474 				// Find inner most first child ex: <p><i><b>*</b></i></p>
17475 				while ((node = node.firstChild)) {
17476 					if (dom.isBlock(node)) {
17477 						return;
17478 					}
17479 
17480 					if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
17481 						firstChilds.push(node);
17482 					}
17483 				}
17484 
17485 				i = firstChilds.length;
17486 				while (i--) {
17487 					node = firstChilds[i];
17488 					if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
17489 						dom.remove(node);
17490 					} else {
17491 						// Remove <a> </a> see #5381
17492 						if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {
17493 							dom.remove(node);
17494 						}
17495 					}
17496 				}
17497 			}
17498 
17499 			// Moves the caret to a suitable position within the root for example in the first non
17500 			// pure whitespace text node or before an image
17501 			function moveToCaretPosition(root) {
17502 				var walker, node, rng, lastNode = root, tempElm;
17503 
17504 				function firstNonWhiteSpaceNodeSibling(node) {
17505 					while (node) {
17506 						if (node.nodeType == 1 || (node.nodeType == 3 && node.data && /[\r\n\s]/.test(node.data))) {
17507 							return node;
17508 						}
17509 
17510 						node = node.nextSibling;
17511 					}
17512 				}
17513 
17514 				// Old IE versions doesn't properly render blocks with br elements in them
17515 				// For example <p><br></p> wont be rendered correctly in a contentEditable area
17516 				// until you remove the br producing <p></p>
17517 				if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) {
17518 					if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') {
17519 						dom.remove(parentBlock.firstChild);
17520 					}
17521 				}
17522 
17523 				if (root.nodeName == 'LI') {
17524 					var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
17525 
17526 					if (firstChild && /^(UL|OL)$/.test(firstChild.nodeName)) {
17527 						root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild);
17528 					}
17529 				}
17530 
17531 				rng = dom.createRng();
17532 
17533 				if (root.hasChildNodes()) {
17534 					walker = new TreeWalker(root, root);
17535 
17536 					while ((node = walker.current())) {
17537 						if (node.nodeType == 3) {
17538 							rng.setStart(node, 0);
17539 							rng.setEnd(node, 0);
17540 							break;
17541 						}
17542 
17543 						if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
17544 							rng.setStartBefore(node);
17545 							rng.setEndBefore(node);
17546 							break;
17547 						}
17548 
17549 						lastNode = node;
17550 						node = walker.next();
17551 					}
17552 
17553 					if (!node) {
17554 						rng.setStart(lastNode, 0);
17555 						rng.setEnd(lastNode, 0);
17556 					}
17557 				} else {
17558 					if (root.nodeName == 'BR') {
17559 						if (root.nextSibling && dom.isBlock(root.nextSibling)) {
17560 							// Trick on older IE versions to render the caret before the BR between two lists
17561 							if (!documentMode || documentMode < 9) {
17562 								tempElm = dom.create('br');
17563 								root.parentNode.insertBefore(tempElm, root);
17564 							}
17565 
17566 							rng.setStartBefore(root);
17567 							rng.setEndBefore(root);
17568 						} else {
17569 							rng.setStartAfter(root);
17570 							rng.setEndAfter(root);
17571 						}
17572 					} else {
17573 						rng.setStart(root, 0);
17574 						rng.setEnd(root, 0);
17575 					}
17576 				}
17577 
17578 				selection.setRng(rng);
17579 
17580 				// Remove tempElm created for old IE:s
17581 				dom.remove(tempElm);
17582 				selection.scrollIntoView(root);
17583 			}
17584 
17585 			function setForcedBlockAttrs(node) {
17586 				var forcedRootBlockName = settings.forced_root_block;
17587 
17588 				if (forcedRootBlockName && forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) {
17589 					dom.setAttribs(node, settings.forced_root_block_attrs);
17590 				}
17591 			}
17592 
17593 			// Creates a new block element by cloning the current one or creating a new one if the name is specified
17594 			// This function will also copy any text formatting from the parent block and add it to the new one
17595 			function createNewBlock(name) {
17596 				var node = container, block, clonedNode, caretNode;
17597 
17598 				if (name || parentBlockName == "TABLE") {
17599 					block = dom.create(name || newBlockName);
17600 					setForcedBlockAttrs(block);
17601 				} else {
17602 					block = parentBlock.cloneNode(false);
17603 				}
17604 
17605 				caretNode = block;
17606 
17607 				// Clone any parent styles
17608 				if (settings.keep_styles !== false) {
17609 					do {
17610 						if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U|VAR|CITE|DFN|CODE|MARK|Q|SUP|SUB|SAMP)$/.test(node.nodeName)) {
17611 							// Never clone a caret containers
17612 							if (node.id == '_mce_caret') {
17613 								continue;
17614 							}
17615 
17616 							clonedNode = node.cloneNode(false);
17617 							dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique
17618 
17619 							if (block.hasChildNodes()) {
17620 								clonedNode.appendChild(block.firstChild);
17621 								block.appendChild(clonedNode);
17622 							} else {
17623 								caretNode = clonedNode;
17624 								block.appendChild(clonedNode);
17625 							}
17626 						}
17627 					} while ((node = node.parentNode));
17628 				}
17629 
17630 				// BR is needed in empty blocks on non IE browsers
17631 				if (!isIE) {
17632 					caretNode.innerHTML = '<br data-mce-bogus="1">';
17633 				}
17634 
17635 				return block;
17636 			}
17637 
17638 			// Returns true/false if the caret is at the start/end of the parent block element
17639 			function isCaretAtStartOrEndOfBlock(start) {
17640 				var walker, node, name;
17641 
17642 				// Caret is in the middle of a text node like "a|b"
17643 				if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
17644 					return false;
17645 				}
17646 
17647 				// If after the last element in block node edge case for #5091
17648 				if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {
17649 					return true;
17650 				}
17651 
17652 				// If the caret if before the first element in parentBlock
17653 				if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
17654 					return true;
17655 				}
17656 
17657 				// Caret can be before/after a table
17658 				if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
17659 					return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
17660 				}
17661 
17662 				// Walk the DOM and look for text nodes or non empty elements
17663 				walker = new TreeWalker(container, parentBlock);
17664 
17665 				// If caret is in beginning or end of a text block then jump to the next/previous node
17666 				if (container.nodeType == 3) {
17667 					if (start && offset === 0) {
17668 						walker.prev();
17669 					} else if (!start && offset == container.nodeValue.length) {
17670 						walker.next();
17671 					}
17672 				}
17673 
17674 				while ((node = walker.current())) {
17675 					if (node.nodeType === 1) {
17676 						// Ignore bogus elements
17677 						if (!node.getAttribute('data-mce-bogus')) {
17678 							// Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
17679 							name = node.nodeName.toLowerCase();
17680 							if (nonEmptyElementsMap[name] && name !== 'br') {
17681 								return false;
17682 							}
17683 						}
17684 					} else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
17685 						return false;
17686 					}
17687 
17688 					if (start) {
17689 						walker.prev();
17690 					} else {
17691 						walker.next();
17692 					}
17693 				}
17694 
17695 				return true;
17696 			}
17697 
17698 			// Wraps any text nodes or inline elements in the specified forced root block name
17699 			function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
17700 				var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P';
17701 
17702 				// Not in a block element or in a table cell or caption
17703 				parentBlock = dom.getParent(container, dom.isBlock);
17704 				rootBlockName = editor.getBody().nodeName.toLowerCase();
17705 				if (!parentBlock || !canSplitBlock(parentBlock)) {
17706 					parentBlock = parentBlock || editableRoot;
17707 
17708 					if (!parentBlock.hasChildNodes()) {
17709 						newBlock = dom.create(blockName);
17710 						setForcedBlockAttrs(newBlock);
17711 						parentBlock.appendChild(newBlock);
17712 						rng.setStart(newBlock, 0);
17713 						rng.setEnd(newBlock, 0);
17714 						return newBlock;
17715 					}
17716 
17717 					// Find parent that is the first child of parentBlock
17718 					node = container;
17719 					while (node.parentNode != parentBlock) {
17720 						node = node.parentNode;
17721 					}
17722 
17723 					// Loop left to find start node start wrapping at
17724 					while (node && !dom.isBlock(node)) {
17725 						startNode = node;
17726 						node = node.previousSibling;
17727 					}
17728 
17729 					if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) {
17730 						newBlock = dom.create(blockName);
17731 						setForcedBlockAttrs(newBlock);
17732 						startNode.parentNode.insertBefore(newBlock, startNode);
17733 
17734 						// Start wrapping until we hit a block
17735 						node = startNode;
17736 						while (node && !dom.isBlock(node)) {
17737 							next = node.nextSibling;
17738 							newBlock.appendChild(node);
17739 							node = next;
17740 						}
17741 
17742 						// Restore range to it's past location
17743 						rng.setStart(container, offset);
17744 						rng.setEnd(container, offset);
17745 					}
17746 				}
17747 
17748 				return container;
17749 			}
17750 
17751 			// Inserts a block or br before/after or in the middle of a split list of the LI is empty
17752 			function handleEmptyListItem() {
17753 				function isFirstOrLastLi(first) {
17754 					var node = containerBlock[first ? 'firstChild' : 'lastChild'];
17755 
17756 					// Find first/last element since there might be whitespace there
17757 					while (node) {
17758 						if (node.nodeType == 1) {
17759 							break;
17760 						}
17761 
17762 						node = node[first ? 'nextSibling' : 'previousSibling'];
17763 					}
17764 
17765 					return node === parentBlock;
17766 				}
17767 
17768 				function getContainerBlock() {
17769 					var containerBlockParent = containerBlock.parentNode;
17770 
17771 					if (containerBlockParent.nodeName == 'LI') {
17772 						return containerBlockParent;
17773 					}
17774 
17775 					return containerBlock;
17776 				}
17777 
17778 				// Check if we are in an nested list
17779 				var containerBlockParentName = containerBlock.parentNode.nodeName;
17780 				if (/^(OL|UL|LI)$/.test(containerBlockParentName)) {
17781 					newBlockName = 'LI';
17782 				}
17783 
17784 				newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');
17785 
17786 				if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
17787 					if (containerBlockParentName == 'LI') {
17788 						// Nested list is inside a LI
17789 						dom.insertAfter(newBlock, getContainerBlock());
17790 					} else {
17791 						// Is first and last list item then replace the OL/UL with a text block
17792 						dom.replace(newBlock, containerBlock);
17793 					}
17794 				} else if (isFirstOrLastLi(true)) {
17795 					if (containerBlockParentName == 'LI') {
17796 						// List nested in an LI then move the list to a new sibling LI
17797 						dom.insertAfter(newBlock, getContainerBlock());
17798 						newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed
17799 						newBlock.appendChild(containerBlock);
17800 					} else {
17801 						// First LI in list then remove LI and add text block before list
17802 						containerBlock.parentNode.insertBefore(newBlock, containerBlock);
17803 					}
17804 				} else if (isFirstOrLastLi()) {
17805 					// Last LI in list then remove LI and add text block after list
17806 					dom.insertAfter(newBlock, getContainerBlock());
17807 					renderBlockOnIE(newBlock);
17808 				} else {
17809 					// Middle LI in list the split the list and insert a text block in the middle
17810 					// Extract after fragment and insert it after the current block
17811 					containerBlock = getContainerBlock();
17812 					tmpRng = rng.cloneRange();
17813 					tmpRng.setStartAfter(parentBlock);
17814 					tmpRng.setEndAfter(containerBlock);
17815 					fragment = tmpRng.extractContents();
17816 
17817 					if (newBlockName == 'LI' && fragment.firstChild.nodeName == 'LI') {
17818 						newBlock = fragment.firstChild;
17819 						dom.insertAfter(fragment, containerBlock);
17820 					} else {
17821 						dom.insertAfter(fragment, containerBlock);
17822 						dom.insertAfter(newBlock, containerBlock);
17823 					}
17824 				}
17825 
17826 				dom.remove(parentBlock);
17827 				moveToCaretPosition(newBlock);
17828 				undoManager.add();
17829 			}
17830 
17831 			// Walks the parent block to the right and look for BR elements
17832 			function hasRightSideContent() {
17833 				var walker = new TreeWalker(container, parentBlock), node;
17834 
17835 				while ((node = walker.next())) {
17836 					if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) {
17837 						return true;
17838 					}
17839 				}
17840 			}
17841 
17842 			// Inserts a BR element if the forced_root_block option is set to false or empty string
17843 			function insertBr() {
17844 				var brElm, extraBr, marker;
17845 
17846 				if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
17847 					// Insert extra BR element at the end block elements
17848 					if (!isIE && !hasRightSideContent()) {
17849 						brElm = dom.create('br');
17850 						rng.insertNode(brElm);
17851 						rng.setStartAfter(brElm);
17852 						rng.setEndAfter(brElm);
17853 						extraBr = true;
17854 					}
17855 				}
17856 
17857 				brElm = dom.create('br');
17858 				rng.insertNode(brElm);
17859 
17860 				// Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it
17861 				if (isIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
17862 					brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
17863 				}
17864 
17865 				// Insert temp marker and scroll to that
17866 				marker = dom.create('span', {}, ' ');
17867 				brElm.parentNode.insertBefore(marker, brElm);
17868 				selection.scrollIntoView(marker);
17869 				dom.remove(marker);
17870 
17871 				if (!extraBr) {
17872 					rng.setStartAfter(brElm);
17873 					rng.setEndAfter(brElm);
17874 				} else {
17875 					rng.setStartBefore(brElm);
17876 					rng.setEndBefore(brElm);
17877 				}
17878 
17879 				selection.setRng(rng);
17880 				undoManager.add();
17881 			}
17882 
17883 			// Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element
17884 			function trimLeadingLineBreaks(node) {
17885 				do {
17886 					if (node.nodeType === 3) {
17887 						node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');
17888 					}
17889 
17890 					node = node.firstChild;
17891 				} while (node);
17892 			}
17893 
17894 			function getEditableRoot(node) {
17895 				var root = dom.getRoot(), parent, editableRoot;
17896 
17897 				// Get all parents until we hit a non editable parent or the root
17898 				parent = node;
17899 				while (parent !== root && dom.getContentEditable(parent) !== "false") {
17900 					if (dom.getContentEditable(parent) === "true") {
17901 						editableRoot = parent;
17902 					}
17903 
17904 					parent = parent.parentNode;
17905 				}
17906 
17907 				return parent !== root ? editableRoot : root;
17908 			}
17909 
17910 			// Adds a BR at the end of blocks that only contains an IMG or INPUT since
17911 			// these might be floated and then they won't expand the block
17912 			function addBrToBlockIfNeeded(block) {
17913 				var lastChild;
17914 
17915 				// IE will render the blocks correctly other browsers needs a BR
17916 				if (!isIE) {
17917 					block.normalize(); // Remove empty text nodes that got left behind by the extract
17918 
17919 					// Check if the block is empty or contains a floated last child
17920 					lastChild = block.lastChild;
17921 					if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
17922 						dom.add(block, 'br');
17923 					}
17924 				}
17925 			}
17926 
17927 			rng = selection.getRng(true);
17928 
17929 			// Event is blocked by some other handler for example the lists plugin
17930 			if (evt.isDefaultPrevented()) {
17931 				return;
17932 			}
17933 
17934 			// Delete any selected contents
17935 			if (!rng.collapsed) {
17936 				editor.execCommand('Delete');
17937 				return;
17938 			}
17939 
17940 			// Setup range items and newBlockName
17941 			new RangeUtils(dom).normalize(rng);
17942 			container = rng.startContainer;
17943 			offset = rng.startOffset;
17944 			newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block;
17945 			newBlockName = newBlockName ? newBlockName.toUpperCase() : '';
17946 			documentMode = dom.doc.documentMode;
17947 			shiftKey = evt.shiftKey;
17948 
17949 			// Resolve node index
17950 			if (container.nodeType == 1 && container.hasChildNodes()) {
17951 				isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
17952 
17953 				container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
17954 				if (isAfterLastNodeInContainer && container.nodeType == 3) {
17955 					offset = container.nodeValue.length;
17956 				} else {
17957 					offset = 0;
17958 				}
17959 			}
17960 
17961 			// Get editable root node normaly the body element but sometimes a div or span
17962 			editableRoot = getEditableRoot(container);
17963 
17964 			// If there is no editable root then enter is done inside a contentEditable false element
17965 			if (!editableRoot) {
17966 				return;
17967 			}
17968 
17969 			undoManager.beforeChange();
17970 
17971 			// If editable root isn't block nor the root of the editor
17972 			if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {
17973 				if (!newBlockName || shiftKey) {
17974 					insertBr();
17975 				}
17976 
17977 				return;
17978 			}
17979 
17980 			// Wrap the current node and it's sibling in a default block if it's needed.
17981 			// for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
17982 			// This won't happen if root blocks are disabled or the shiftKey is pressed
17983 			if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) {
17984 				container = wrapSelfAndSiblingsInDefaultBlock(container, offset);
17985 			}
17986 
17987 			// Find parent block and setup empty block paddings
17988 			parentBlock = dom.getParent(container, dom.isBlock);
17989 			containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
17990 
17991 			// Setup block names
17992 			parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
17993 			containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
17994 
17995 			// Enter inside block contained within a LI then split or insert before/after LI
17996 			if (containerBlockName == 'LI' && !evt.ctrlKey) {
17997 				parentBlock = containerBlock;
17998 				parentBlockName = containerBlockName;
17999 			}
18000 
18001 			// Handle enter in LI
18002 			if (parentBlockName == 'LI') {
18003 				if (!newBlockName && shiftKey) {
18004 					insertBr();
18005 					return;
18006 				}
18007 
18008 				// Handle enter inside an empty list item
18009 				if (dom.isEmpty(parentBlock)) {
18010 					handleEmptyListItem();
18011 					return;
18012 				}
18013 			}
18014 
18015 			// Don't split PRE tags but insert a BR instead easier when writing code samples etc
18016 			if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {
18017 				if (!shiftKey) {
18018 					insertBr();
18019 					return;
18020 				}
18021 			} else {
18022 				// If no root block is configured then insert a BR by default or if the shiftKey is pressed
18023 				if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) {
18024 					insertBr();
18025 					return;
18026 				}
18027 			}
18028 
18029 			// If parent block is root then never insert new blocks
18030 			if (newBlockName && parentBlock === editor.getBody()) {
18031 				return;
18032 			}
18033 
18034 			// Default block name if it's not configured
18035 			newBlockName = newBlockName || 'P';
18036 
18037 			// Insert new block before/after the parent block depending on caret location
18038 			if (isCaretAtStartOrEndOfBlock()) {
18039 				// If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup
18040 				if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
18041 					newBlock = createNewBlock(newBlockName);
18042 				} else {
18043 					newBlock = createNewBlock();
18044 				}
18045 
18046 				// Split the current container block element if enter is pressed inside an empty inner block element
18047 				if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {
18048 					// Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
18049 					newBlock = dom.split(containerBlock, parentBlock);
18050 				} else {
18051 					dom.insertAfter(newBlock, parentBlock);
18052 				}
18053 
18054 				moveToCaretPosition(newBlock);
18055 			} else if (isCaretAtStartOrEndOfBlock(true)) {
18056 				// Insert new block before
18057 				newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
18058 				renderBlockOnIE(newBlock);
18059 				moveToCaretPosition(parentBlock);
18060 			} else {
18061 				// Extract after fragment and insert it after the current block
18062 				tmpRng = rng.cloneRange();
18063 				tmpRng.setEndAfter(parentBlock);
18064 				fragment = tmpRng.extractContents();
18065 				trimLeadingLineBreaks(fragment);
18066 				newBlock = fragment.firstChild;
18067 				dom.insertAfter(fragment, parentBlock);
18068 				trimInlineElementsOnLeftSideOfBlock(newBlock);
18069 				addBrToBlockIfNeeded(parentBlock);
18070 				moveToCaretPosition(newBlock);
18071 			}
18072 
18073 			dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
18074 
18075 			// Allow custom handling of new blocks
18076 			editor.fire('NewBlock', { newBlock: newBlock });
18077 
18078 			undoManager.add();
18079 		}
18080 
18081 		editor.on('keydown', function(evt) {
18082 			if (evt.keyCode == 13) {
18083 				if (handleEnterKey(evt) !== false) {
18084 					evt.preventDefault();
18085 				}
18086 			}
18087 		});
18088 	};
18089 });
18090 
18091 // Included from: js/tinymce/classes/ForceBlocks.js
18092 
18093 /**
18094  * ForceBlocks.js
18095  *
18096  * Copyright, Moxiecode Systems AB
18097  * Released under LGPL License.
18098  *
18099  * License: http://www.tinymce.com/license
18100  * Contributing: http://www.tinymce.com/contributing
18101  */
18102 
18103 define("tinymce/ForceBlocks", [], function() {
18104 	return function(editor) {
18105 		var settings = editor.settings, dom = editor.dom, selection = editor.selection;
18106 		var schema = editor.schema, blockElements = schema.getBlockElements();
18107 
18108 		function addRootBlocks() {
18109 			var node = selection.getStart(), rootNode = editor.getBody(), rng;
18110 			var startContainer, startOffset, endContainer, endOffset, rootBlockNode;
18111 			var tempNode, offset = -0xFFFFFF, wrapped, restoreSelection;
18112 			var tmpRng, rootNodeName, forcedRootBlock;
18113 
18114 			forcedRootBlock = settings.forced_root_block;
18115 
18116 			if (!node || node.nodeType !== 1 || !forcedRootBlock) {
18117 				return;
18118 			}
18119 
18120 			// Check if node is wrapped in block
18121 			while (node && node != rootNode) {
18122 				if (blockElements[node.nodeName]) {
18123 					return;
18124 				}
18125 
18126 				node = node.parentNode;
18127 			}
18128 
18129 			// Get current selection
18130 			rng = selection.getRng();
18131 			if (rng.setStart) {
18132 				startContainer = rng.startContainer;
18133 				startOffset = rng.startOffset;
18134 				endContainer = rng.endContainer;
18135 				endOffset = rng.endOffset;
18136 
18137 				try {
18138 					restoreSelection = editor.getDoc().activeElement === rootNode;
18139 				} catch (ex) {
18140 					// IE throws unspecified error here sometimes
18141 				}
18142 			} else {
18143 				// Force control range into text range
18144 				if (rng.item) {
18145 					node = rng.item(0);
18146 					rng = editor.getDoc().body.createTextRange();
18147 					rng.moveToElementText(node);
18148 				}
18149 
18150 				restoreSelection = rng.parentElement().ownerDocument === editor.getDoc();
18151 				tmpRng = rng.duplicate();
18152 				tmpRng.collapse(true);
18153 				startOffset = tmpRng.move('character', offset) * -1;
18154 
18155 				if (!tmpRng.collapsed) {
18156 					tmpRng = rng.duplicate();
18157 					tmpRng.collapse(false);
18158 					endOffset = (tmpRng.move('character', offset) * -1) - startOffset;
18159 				}
18160 			}
18161 
18162 			// Wrap non block elements and text nodes
18163 			node = rootNode.firstChild;
18164 			rootNodeName = rootNode.nodeName.toLowerCase();
18165 			while (node) {
18166 				// TODO: Break this up, too complex
18167 				if (((node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName]))) &&
18168 					schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase())) {
18169 					// Remove empty text nodes
18170 					if (node.nodeType === 3 && node.nodeValue.length === 0) {
18171 						tempNode = node;
18172 						node = node.nextSibling;
18173 						dom.remove(tempNode);
18174 						continue;
18175 					}
18176 
18177 					if (!rootBlockNode) {
18178 						rootBlockNode = dom.create(forcedRootBlock, editor.settings.forced_root_block_attrs);
18179 						node.parentNode.insertBefore(rootBlockNode, node);
18180 						wrapped = true;
18181 					}
18182 
18183 					tempNode = node;
18184 					node = node.nextSibling;
18185 					rootBlockNode.appendChild(tempNode);
18186 				} else {
18187 					rootBlockNode = null;
18188 					node = node.nextSibling;
18189 				}
18190 			}
18191 
18192 			if (wrapped && restoreSelection) {
18193 				if (rng.setStart) {
18194 					rng.setStart(startContainer, startOffset);
18195 					rng.setEnd(endContainer, endOffset);
18196 					selection.setRng(rng);
18197 				} else {
18198 					// Only select if the previous selection was inside the document to prevent auto focus in quirks mode
18199 					try {
18200 						rng = editor.getDoc().body.createTextRange();
18201 						rng.moveToElementText(rootNode);
18202 						rng.collapse(true);
18203 						rng.moveStart('character', startOffset);
18204 
18205 						if (endOffset > 0) {
18206 							rng.moveEnd('character', endOffset);
18207 						}
18208 
18209 						rng.select();
18210 					} catch (ex) {
18211 						// Ignore
18212 					}
18213 				}
18214 
18215 				editor.nodeChanged();
18216 			}
18217 		}
18218 
18219 		// Force root blocks
18220 		if (settings.forced_root_block) {
18221 			editor.on('NodeChange', addRootBlocks);
18222 		}
18223 	};
18224 });
18225 
18226 // Included from: js/tinymce/classes/EditorCommands.js
18227 
18228 /**
18229  * EditorCommands.js
18230  *
18231  * Copyright, Moxiecode Systems AB
18232  * Released under LGPL License.
18233  *
18234  * License: http://www.tinymce.com/license
18235  * Contributing: http://www.tinymce.com/contributing
18236  */
18237 
18238 /**
18239  * This class enables you to add custom editor commands and it contains
18240  * overrides for native browser commands to address various bugs and issues.
18241  *
18242  * @class tinymce.EditorCommands
18243  */
18244 define("tinymce/EditorCommands", [
18245 	"tinymce/html/Serializer",
18246 	"tinymce/Env",
18247 	"tinymce/util/Tools"
18248 ], function(Serializer, Env, Tools) {
18249 	// Added for compression purposes
18250 	var each = Tools.each, extend = Tools.extend;
18251 	var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
18252 	var isGecko = Env.gecko, isIE = Env.ie;
18253 	var TRUE = true, FALSE = false;
18254 
18255 	return function(editor) {
18256 		var dom = editor.dom,
18257 			selection = editor.selection,
18258 			commands = {state: {}, exec: {}, value: {}},
18259 			settings = editor.settings,
18260 			formatter = editor.formatter,
18261 			bookmark;
18262 
18263 		/**
18264 		 * Executes the specified command.
18265 		 *
18266 		 * @method execCommand
18267 		 * @param {String} command Command to execute.
18268 		 * @param {Boolean} ui Optional user interface state.
18269 		 * @param {Object} value Optional value for command.
18270 		 * @return {Boolean} true/false if the command was found or not.
18271 		 */
18272 		function execCommand(command, ui, value) {
18273 			var func;
18274 
18275 			command = command.toLowerCase();
18276 			if ((func = commands.exec[command])) {
18277 				func(command, ui, value);
18278 				return TRUE;
18279 			}
18280 
18281 			return FALSE;
18282 		}
18283 
18284 		/**
18285 		 * Queries the current state for a command for example if the current selection is "bold".
18286 		 *
18287 		 * @method queryCommandState
18288 		 * @param {String} command Command to check the state of.
18289 		 * @return {Boolean/Number} true/false if the selected contents is bold or not, -1 if it's not found.
18290 		 */
18291 		function queryCommandState(command) {
18292 			var func;
18293 
18294 			command = command.toLowerCase();
18295 			if ((func = commands.state[command])) {
18296 				return func(command);
18297 			}
18298 
18299 			return -1;
18300 		}
18301 
18302 		/**
18303 		 * Queries the command value for example the current fontsize.
18304 		 *
18305 		 * @method queryCommandValue
18306 		 * @param {String} command Command to check the value of.
18307 		 * @return {Object} Command value of false if it's not found.
18308 		 */
18309 		function queryCommandValue(command) {
18310 			var func;
18311 
18312 			command = command.toLowerCase();
18313 			if ((func = commands.value[command])) {
18314 				return func(command);
18315 			}
18316 
18317 			return FALSE;
18318 		}
18319 
18320 		/**
18321 		 * Adds commands to the command collection.
18322 		 *
18323 		 * @method addCommands
18324 		 * @param {Object} command_list Name/value collection with commands to add, the names can also be comma separated.
18325 		 * @param {String} type Optional type to add, defaults to exec. Can be value or state as well.
18326 		 */
18327 		function addCommands(command_list, type) {
18328 			type = type || 'exec';
18329 
18330 			each(command_list, function(callback, command) {
18331 				each(command.toLowerCase().split(','), function(command) {
18332 					commands[type][command] = callback;
18333 				});
18334 			});
18335 		}
18336 
18337 		// Expose public methods
18338 		extend(this, {
18339 			execCommand: execCommand,
18340 			queryCommandState: queryCommandState,
18341 			queryCommandValue: queryCommandValue,
18342 			addCommands: addCommands
18343 		});
18344 
18345 		// Private methods
18346 
18347 		function execNativeCommand(command, ui, value) {
18348 			if (ui === undefined) {
18349 				ui = FALSE;
18350 			}
18351 
18352 			if (value === undefined) {
18353 				value = null;
18354 			}
18355 
18356 			return editor.getDoc().execCommand(command, ui, value);
18357 		}
18358 
18359 		function isFormatMatch(name) {
18360 			return formatter.match(name);
18361 		}
18362 
18363 		function toggleFormat(name, value) {
18364 			formatter.toggle(name, value ? {value: value} : undefined);
18365 			editor.nodeChanged();
18366 		}
18367 
18368 		function storeSelection(type) {
18369 			bookmark = selection.getBookmark(type);
18370 		}
18371 
18372 		function restoreSelection() {
18373 			selection.moveToBookmark(bookmark);
18374 		}
18375 
18376 		// Add execCommand overrides
18377 		addCommands({
18378 			// Ignore these, added for compatibility
18379 			'mceResetDesignMode,mceBeginUndoLevel': function() {},
18380 
18381 			// Add undo manager logic
18382 			'mceEndUndoLevel,mceAddUndoLevel': function() {
18383 				editor.undoManager.add();
18384 			},
18385 
18386 			'Cut,Copy,Paste': function(command) {
18387 				var doc = editor.getDoc(), failed;
18388 
18389 				// Try executing the native command
18390 				try {
18391 					execNativeCommand(command);
18392 				} catch (ex) {
18393 					// Command failed
18394 					failed = TRUE;
18395 				}
18396 
18397 				// Present alert message about clipboard access not being available
18398 				if (failed || !doc.queryCommandSupported(command)) {
18399 					var msg = editor.translate(
18400 						"Your browser doesn't support direct access to the clipboard. " +
18401 						"Please use the Ctrl+X/C/V keyboard shortcuts instead."
18402 					);
18403 
18404 					if (Env.mac) {
18405 						msg = msg.replace(/Ctrl\+/g, '\u2318+');
18406 					}
18407 
18408 					editor.windowManager.alert(msg);
18409 				}
18410 			},
18411 
18412 			// Override unlink command
18413 			unlink: function() {
18414 				if (selection.isCollapsed()) {
18415 					var elm = selection.getNode();
18416 					if (elm.tagName == 'A') {
18417 						editor.dom.remove(elm, true);
18418 					}
18419 
18420 					return;
18421 				}
18422 
18423 				formatter.remove("link");
18424 			},
18425 
18426 			// Override justify commands to use the text formatter engine
18427 			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
18428 				var align = command.substring(7);
18429 
18430 				if (align == 'full') {
18431 					align = 'justify';
18432 				}
18433 
18434 				// Remove all other alignments first
18435 				each('left,center,right,justify'.split(','), function(name) {
18436 					if (align != name) {
18437 						formatter.remove('align' + name);
18438 					}
18439 				});
18440 
18441 				toggleFormat('align' + align);
18442 				execCommand('mceRepaint');
18443 			},
18444 
18445 			// Override list commands to fix WebKit bug
18446 			'InsertUnorderedList,InsertOrderedList': function(command) {
18447 				var listElm, listParent;
18448 
18449 				execNativeCommand(command);
18450 
18451 				// WebKit produces lists within block elements so we need to split them
18452 				// we will replace the native list creation logic to custom logic later on
18453 				// TODO: Remove this when the list creation logic is removed
18454 				listElm = dom.getParent(selection.getNode(), 'ol,ul');
18455 				if (listElm) {
18456 					listParent = listElm.parentNode;
18457 
18458 					// If list is within a text block then split that block
18459 					if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) {
18460 						storeSelection();
18461 						dom.split(listParent, listElm);
18462 						restoreSelection();
18463 					}
18464 				}
18465 			},
18466 
18467 			// Override commands to use the text formatter engine
18468 			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
18469 				toggleFormat(command);
18470 			},
18471 
18472 			// Override commands to use the text formatter engine
18473 			'ForeColor,HiliteColor,FontName': function(command, ui, value) {
18474 				toggleFormat(command, value);
18475 			},
18476 
18477 			FontSize: function(command, ui, value) {
18478 				var fontClasses, fontSizes;
18479 
18480 				// Convert font size 1-7 to styles
18481 				if (value >= 1 && value <= 7) {
18482 					fontSizes = explode(settings.font_size_style_values);
18483 					fontClasses = explode(settings.font_size_classes);
18484 
18485 					if (fontClasses) {
18486 						value = fontClasses[value - 1] || value;
18487 					} else {
18488 						value = fontSizes[value - 1] || value;
18489 					}
18490 				}
18491 
18492 				toggleFormat(command, value);
18493 			},
18494 
18495 			RemoveFormat: function(command) {
18496 				formatter.remove(command);
18497 			},
18498 
18499 			mceBlockQuote: function() {
18500 				toggleFormat('blockquote');
18501 			},
18502 
18503 			FormatBlock: function(command, ui, value) {
18504 				return toggleFormat(value || 'p');
18505 			},
18506 
18507 			mceCleanup: function() {
18508 				var bookmark = selection.getBookmark();
18509 
18510 				editor.setContent(editor.getContent({cleanup: TRUE}), {cleanup: TRUE});
18511 
18512 				selection.moveToBookmark(bookmark);
18513 			},
18514 
18515 			mceRemoveNode: function(command, ui, value) {
18516 				var node = value || selection.getNode();
18517 
18518 				// Make sure that the body node isn't removed
18519 				if (node != editor.getBody()) {
18520 					storeSelection();
18521 					editor.dom.remove(node, TRUE);
18522 					restoreSelection();
18523 				}
18524 			},
18525 
18526 			mceSelectNodeDepth: function(command, ui, value) {
18527 				var counter = 0;
18528 
18529 				dom.getParent(selection.getNode(), function(node) {
18530 					if (node.nodeType == 1 && counter++ == value) {
18531 						selection.select(node);
18532 						return FALSE;
18533 					}
18534 				}, editor.getBody());
18535 			},
18536 
18537 			mceSelectNode: function(command, ui, value) {
18538 				selection.select(value);
18539 			},
18540 
18541 			mceInsertContent: function(command, ui, value) {
18542 				var parser, serializer, parentNode, rootNode, fragment, args;
18543 				var marker, rng, node, node2, bookmarkHtml;
18544 
18545 				function trimOrPaddLeftRight(html) {
18546 					var rng, container, offset;
18547 
18548 					rng = selection.getRng(true);
18549 					container = rng.startContainer;
18550 					offset = rng.startOffset;
18551 
18552 					function hasSiblingText(siblingName) {
18553 						return container[siblingName] && container[siblingName].nodeType == 3;
18554 					}
18555 
18556 					if (container.nodeType == 3) {
18557 						if (offset > 0) {
18558 							html = html.replace(/^ /, ' ');
18559 						} else if (!hasSiblingText('previousSibling')) {
18560 							html = html.replace(/^ /, ' ');
18561 						}
18562 
18563 						if (offset < container.length) {
18564 							html = html.replace(/ (<br>|)$/, ' ');
18565 						} else if (!hasSiblingText('nextSibling')) {
18566 							html = html.replace(/( | )(<br>|)$/, ' ');
18567 						}
18568 					}
18569 
18570 					return html;
18571 				}
18572 
18573 				// Check for whitespace before/after value
18574 				if (/^ | $/.test(value)) {
18575 					value = trimOrPaddLeftRight(value);
18576 				}
18577 
18578 				// Setup parser and serializer
18579 				parser = editor.parser;
18580 				serializer = new Serializer({}, editor.schema);
18581 				bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">ÈB;</span>';
18582 
18583 				// Run beforeSetContent handlers on the HTML to be inserted
18584 				args = {content: value, format: 'html', selection: true};
18585 				editor.fire('BeforeSetContent', args);
18586 				value = args.content;
18587 
18588 				// Add caret at end of contents if it's missing
18589 				if (value.indexOf('{$caret}') == -1) {
18590 					value += '{$caret}';
18591 				}
18592 
18593 				// Replace the caret marker with a span bookmark element
18594 				value = value.replace(/\{\$caret\}/, bookmarkHtml);
18595 
18596 				// If selection is at <body>|<p></p> then move it into <body><p>|</p>
18597 				rng = selection.getRng();
18598 				var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null);
18599 				var body = editor.getBody();
18600 				if (caretElement === body && selection.isCollapsed()) {
18601 					if (dom.isBlock(body.firstChild) && dom.isEmpty(body.firstChild)) {
18602 						rng = dom.createRng();
18603 						rng.setStart(body.firstChild, 0);
18604 						rng.setEnd(body.firstChild, 0);
18605 						selection.setRng(rng);
18606 					}
18607 				}
18608 
18609 				// Insert node maker where we will insert the new HTML and get it's parent
18610 				if (!selection.isCollapsed()) {
18611 					editor.getDoc().execCommand('Delete', false, null);
18612 				}
18613 
18614 				parentNode = selection.getNode();
18615 
18616 				// Parse the fragment within the context of the parent node
18617 				var parserArgs = {context: parentNode.nodeName.toLowerCase()};
18618 				fragment = parser.parse(value, parserArgs);
18619 
18620 				// Move the caret to a more suitable location
18621 				node = fragment.lastChild;
18622 				if (node.attr('id') == 'mce_marker') {
18623 					marker = node;
18624 
18625 					for (node = node.prev; node; node = node.walk(true)) {
18626 						if (node.type == 3 || !dom.isBlock(node.name)) {
18627 							node.parent.insert(marker, node, node.name === 'br');
18628 							break;
18629 						}
18630 					}
18631 				}
18632 
18633 				// If parser says valid we can insert the contents into that parent
18634 				if (!parserArgs.invalid) {
18635 					value = serializer.serialize(fragment);
18636 
18637 					// Check if parent is empty or only has one BR element then set the innerHTML of that parent
18638 					node = parentNode.firstChild;
18639 					node2 = parentNode.lastChild;
18640 					if (!node || (node === node2 && node.nodeName === 'BR')) {
18641 						dom.setHTML(parentNode, value);
18642 					} else {
18643 						selection.setContent(value);
18644 					}
18645 				} else {
18646 					// If the fragment was invalid within that context then we need
18647 					// to parse and process the parent it's inserted into
18648 
18649 					// Insert bookmark node and get the parent
18650 					selection.setContent(bookmarkHtml);
18651 					parentNode = selection.getNode();
18652 					rootNode = editor.getBody();
18653 
18654 					// Opera will return the document node when selection is in root
18655 					if (parentNode.nodeType == 9) {
18656 						parentNode = node = rootNode;
18657 					} else {
18658 						node = parentNode;
18659 					}
18660 
18661 					// Find the ancestor just before the root element
18662 					while (node !== rootNode) {
18663 						parentNode = node;
18664 						node = node.parentNode;
18665 					}
18666 
18667 					// Get the outer/inner HTML depending on if we are in the root and parser and serialize that
18668 					value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode);
18669 					value = serializer.serialize(
18670 						parser.parse(
18671 							// Need to replace by using a function since $ in the contents would otherwise be a problem
18672 							value.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i, function() {
18673 								return serializer.serialize(fragment);
18674 							})
18675 						)
18676 					);
18677 
18678 					// Set the inner/outer HTML depending on if we are in the root or not
18679 					if (parentNode == rootNode) {
18680 						dom.setHTML(rootNode, value);
18681 					} else {
18682 						dom.setOuterHTML(parentNode, value);
18683 					}
18684 				}
18685 
18686 				marker = dom.get('mce_marker');
18687 				selection.scrollIntoView(marker);
18688 
18689 				// Move selection before marker and remove it
18690 				rng = dom.createRng();
18691 
18692 				// If previous sibling is a text node set the selection to the end of that node
18693 				node = marker.previousSibling;
18694 				if (node && node.nodeType == 3) {
18695 					rng.setStart(node, node.nodeValue.length);
18696 
18697 					// TODO: Why can't we normalize on IE
18698 					if (!isIE) {
18699 						node2 = marker.nextSibling;
18700 						if (node2 && node2.nodeType == 3) {
18701 							node.appendData(node2.data);
18702 							node2.parentNode.removeChild(node2);
18703 						}
18704 					}
18705 				} else {
18706 					// If the previous sibling isn't a text node or doesn't exist set the selection before the marker node
18707 					rng.setStartBefore(marker);
18708 					rng.setEndBefore(marker);
18709 				}
18710 
18711 				// Remove the marker node and set the new range
18712 				dom.remove(marker);
18713 				selection.setRng(rng);
18714 
18715 				// Dispatch after event and add any visual elements needed
18716 				editor.fire('SetContent', args);
18717 				editor.addVisual();
18718 			},
18719 
18720 			mceInsertRawHTML: function(command, ui, value) {
18721 				selection.setContent('tiny_mce_marker');
18722 				editor.setContent(
18723 					editor.getContent().replace(/tiny_mce_marker/g, function() {
18724 						return value;
18725 					})
18726 				);
18727 			},
18728 
18729 			mceToggleFormat: function(command, ui, value) {
18730 				toggleFormat(value);
18731 			},
18732 
18733 			mceSetContent: function(command, ui, value) {
18734 				editor.setContent(value);
18735 			},
18736 
18737 			'Indent,Outdent': function(command) {
18738 				var intentValue, indentUnit, value;
18739 
18740 				// Setup indent level
18741 				intentValue = settings.indentation;
18742 				indentUnit = /[a-z%]+$/i.exec(intentValue);
18743 				intentValue = parseInt(intentValue, 10);
18744 
18745 				if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) {
18746 					// If forced_root_blocks is set to false we don't have a block to indent so lets create a div
18747 					if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) {
18748 						formatter.apply('div');
18749 					}
18750 
18751 					each(selection.getSelectedBlocks(), function(element) {
18752 						if (element.nodeName != "LI") {
18753 							var indentStyleName = editor.getParam('indent_use_margin', false) ? 'margin' : 'padding';
18754 
18755 							indentStyleName += dom.getStyle(element, 'direction', true) == 'rtl' ? 'Right' : 'Left';
18756 
18757 							if (command == 'outdent') {
18758 								value = Math.max(0, parseInt(element.style[indentStyleName] || 0, 10) - intentValue);
18759 								dom.setStyle(element, indentStyleName, value ? value + indentUnit : '');
18760 							} else {
18761 								value = (parseInt(element.style[indentStyleName] || 0, 10) + intentValue) + indentUnit;
18762 								dom.setStyle(element, indentStyleName, value);
18763 							}
18764 						}
18765 					});
18766 				} else {
18767 					execNativeCommand(command);
18768 				}
18769 			},
18770 
18771 			mceRepaint: function() {
18772 				if (isGecko) {
18773 					try {
18774 						storeSelection(TRUE);
18775 
18776 						if (selection.getSel()) {
18777 							selection.getSel().selectAllChildren(editor.getBody());
18778 						}
18779 
18780 						selection.collapse(TRUE);
18781 						restoreSelection();
18782 					} catch (ex) {
18783 						// Ignore
18784 					}
18785 				}
18786 			},
18787 
18788 			InsertHorizontalRule: function() {
18789 				editor.execCommand('mceInsertContent', false, '<hr />');
18790 			},
18791 
18792 			mceToggleVisualAid: function() {
18793 				editor.hasVisual = !editor.hasVisual;
18794 				editor.addVisual();
18795 			},
18796 
18797 			mceReplaceContent: function(command, ui, value) {
18798 				editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format: 'text'})));
18799 			},
18800 
18801 			mceInsertLink: function(command, ui, value) {
18802 				var anchor;
18803 
18804 				if (typeof(value) == 'string') {
18805 					value = {href: value};
18806 				}
18807 
18808 				anchor = dom.getParent(selection.getNode(), 'a');
18809 
18810 				// Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.
18811 				value.href = value.href.replace(' ', '%20');
18812 
18813 				// Remove existing links if there could be child links or that the href isn't specified
18814 				if (!anchor || !value.href) {
18815 					formatter.remove('link');
18816 				}
18817 
18818 				// Apply new link to selection
18819 				if (value.href) {
18820 					formatter.apply('link', value, anchor);
18821 				}
18822 			},
18823 
18824 			selectAll: function() {
18825 				var root = dom.getRoot(), rng;
18826 
18827 				if (selection.getRng().setStart) {
18828 					rng = dom.createRng();
18829 					rng.setStart(root, 0);
18830 					rng.setEnd(root, root.childNodes.length);
18831 					selection.setRng(rng);
18832 				} else {
18833 					// IE will render it's own root level block elements and sometimes
18834 					// even put font elements in them when the user starts typing. So we need to
18835 					// move the selection to a more suitable element from this:
18836 					// <body>|<p></p></body> to this: <body><p>|</p></body>
18837 					rng = selection.getRng();
18838 					if (!rng.item) {
18839 						rng.moveToElementText(root);
18840 						rng.select();
18841 					}
18842 				}
18843 			},
18844 
18845 			"delete": function() {
18846 				execNativeCommand("Delete");
18847 
18848 				// Check if body is empty after the delete call if so then set the contents
18849 				// to an empty string and move the caret to any block produced by that operation
18850 				// this fixes the issue with root blocks not being properly produced after a delete call on IE
18851 				var body = editor.getBody();
18852 
18853 				if (dom.isEmpty(body)) {
18854 					editor.setContent('');
18855 
18856 					if (body.firstChild && dom.isBlock(body.firstChild)) {
18857 						editor.selection.setCursorLocation(body.firstChild, 0);
18858 					} else {
18859 						editor.selection.setCursorLocation(body, 0);
18860 					}
18861 				}
18862 			},
18863 
18864 			mceNewDocument: function() {
18865 				editor.setContent('');
18866 			}
18867 		});
18868 
18869 		// Add queryCommandState overrides
18870 		addCommands({
18871 			// Override justify commands
18872 			'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) {
18873 				var name = 'align' + command.substring(7);
18874 				var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();
18875 				var matches = map(nodes, function(node) {
18876 					return !!formatter.matchNode(node, name);
18877 				});
18878 				return inArray(matches, TRUE) !== -1;
18879 			},
18880 
18881 			'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) {
18882 				return isFormatMatch(command);
18883 			},
18884 
18885 			mceBlockQuote: function() {
18886 				return isFormatMatch('blockquote');
18887 			},
18888 
18889 			Outdent: function() {
18890 				var node;
18891 
18892 				if (settings.inline_styles) {
18893 					if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
18894 						return TRUE;
18895 					}
18896 
18897 					if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) {
18898 						return TRUE;
18899 					}
18900 				}
18901 
18902 				return (
18903 					queryCommandState('InsertUnorderedList') ||
18904 					queryCommandState('InsertOrderedList') ||
18905 					(!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE'))
18906 				);
18907 			},
18908 
18909 			'InsertUnorderedList,InsertOrderedList': function(command) {
18910 				var list = dom.getParent(selection.getNode(), 'ul,ol');
18911 
18912 				return list &&
18913 					(
18914 						command === 'insertunorderedlist' && list.tagName === 'UL' ||
18915 						command === 'insertorderedlist' && list.tagName === 'OL'
18916 					);
18917 			}
18918 		}, 'state');
18919 
18920 		// Add queryCommandValue overrides
18921 		addCommands({
18922 			'FontSize,FontName': function(command) {
18923 				var value = 0, parent;
18924 
18925 				if ((parent = dom.getParent(selection.getNode(), 'span'))) {
18926 					if (command == 'fontsize') {
18927 						value = parent.style.fontSize;
18928 					} else {
18929 						value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase();
18930 					}
18931 				}
18932 
18933 				return value;
18934 			}
18935 		}, 'value');
18936 
18937 		// Add undo manager logic
18938 		addCommands({
18939 			Undo: function() {
18940 				editor.undoManager.undo();
18941 			},
18942 
18943 			Redo: function() {
18944 				editor.undoManager.redo();
18945 			}
18946 		});
18947 	};
18948 });
18949 
18950 // Included from: js/tinymce/classes/util/URI.js
18951 
18952 /**
18953  * URI.js
18954  *
18955  * Copyright, Moxiecode Systems AB
18956  * Released under LGPL License.
18957  *
18958  * License: http://www.tinymce.com/license
18959  * Contributing: http://www.tinymce.com/contributing
18960  */
18961 
18962 /**
18963  * This class handles parsing, modification and serialization of URI/URL strings.
18964  * @class tinymce.util.URI
18965  */
18966 define("tinymce/util/URI", [
18967 	"tinymce/util/Tools"
18968 ], function(Tools) {
18969 	var each = Tools.each, trim = Tools.trim,
18970 		DEFAULT_PORTS = {
18971 			'ftp': 21,
18972 			'http': 80,
18973 			'https': 443,
18974 			'mailto': 25
18975 		};
18976 
18977 	/**
18978 	 * Constructs a new URI instance.
18979 	 *
18980 	 * @constructor
18981 	 * @method URI
18982 	 * @param {String} url URI string to parse.
18983 	 * @param {Object} settings Optional settings object.
18984 	 */
18985 	function URI(url, settings) {
18986 		var self = this, baseUri, base_url;
18987 
18988 		// Trim whitespace
18989 		url = trim(url);
18990 
18991 		// Default settings
18992 		settings = self.settings = settings || {};
18993 
18994 		// Strange app protocol that isn't http/https or local anchor
18995 		// For example: mailto,skype,tel etc.
18996 		if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) {
18997 			self.source = url;
18998 			return;
18999 		}
19000 
19001 		var isProtocolRelative = url.indexOf('//') === 0;
19002 
19003 		// Absolute path with no host, fake host and protocol
19004 		if (url.indexOf('/') === 0 && !isProtocolRelative) {
19005 			url = (settings.base_uri ? settings.base_uri.protocol || 'http' : 'http') + '://mce_host' + url;
19006 		}
19007 
19008 		// Relative path http:// or protocol relative //path
19009 		if (!/^[\w\-]*:?\/\//.test(url)) {
19010 			base_url = settings.base_uri ? settings.base_uri.path : new URI(location.href).directory;
19011 			if (settings.base_uri.protocol === "") {
19012 				url = '//mce_host' + self.toAbsPath(base_url, url);
19013 			} else {
19014 				url = ((settings.base_uri && settings.base_uri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url);
19015 			}
19016 		}
19017 
19018 		// Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
19019 		url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
19020 
19021 		/*jshint maxlen: 255 */
19022 		/*eslint max-len: 0 */
19023 		url = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url);
19024 
19025 		each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
19026 			var part = url[i];
19027 
19028 			// Zope 3 workaround, they use @@something
19029 			if (part) {
19030 				part = part.replace(/\(mce_at\)/g, '@@');
19031 			}
19032 
19033 			self[v] = part;
19034 		});
19035 
19036 		baseUri = settings.base_uri;
19037 		if (baseUri) {
19038 			if (!self.protocol) {
19039 				self.protocol = baseUri.protocol;
19040 			}
19041 
19042 			if (!self.userInfo) {
19043 				self.userInfo = baseUri.userInfo;
19044 			}
19045 
19046 			if (!self.port && self.host === 'mce_host') {
19047 				self.port = baseUri.port;
19048 			}
19049 
19050 			if (!self.host || self.host === 'mce_host') {
19051 				self.host = baseUri.host;
19052 			}
19053 
19054 			self.source = '';
19055 		}
19056 
19057 		if (isProtocolRelative) {
19058 			self.protocol = '';
19059 		}
19060 
19061 		//t.path = t.path || '/';
19062 	}
19063 
19064 	URI.prototype = {
19065 		/**
19066 		 * Sets the internal path part of the URI.
19067 		 *
19068 		 * @method setPath
19069 		 * @param {string} path Path string to set.
19070 		 */
19071 		setPath: function(path) {
19072 			var self = this;
19073 
19074 			path = /^(.*?)\/?(\w+)?$/.exec(path);
19075 
19076 			// Update path parts
19077 			self.path = path[0];
19078 			self.directory = path[1];
19079 			self.file = path[2];
19080 
19081 			// Rebuild source
19082 			self.source = '';
19083 			self.getURI();
19084 		},
19085 
19086 		/**
19087 		 * Converts the specified URI into a relative URI based on the current URI instance location.
19088 		 *
19089 		 * @method toRelative
19090 		 * @param {String} uri URI to convert into a relative path/URI.
19091 		 * @return {String} Relative URI from the point specified in the current URI instance.
19092 		 * @example
19093 		 * // Converts an absolute URL to an relative URL url will be somedir/somefile.htm
19094 		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toRelative('http://www.site.com/dir/somedir/somefile.htm');
19095 		 */
19096 		toRelative: function(uri) {
19097 			var self = this, output;
19098 
19099 			if (uri === "./") {
19100 				return uri;
19101 			}
19102 
19103 			uri = new URI(uri, {base_uri: self});
19104 
19105 			// Not on same domain/port or protocol
19106 			if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port ||
19107 				(self.protocol != uri.protocol && uri.protocol !== "")) {
19108 				return uri.getURI();
19109 			}
19110 
19111 			var tu = self.getURI(), uu = uri.getURI();
19112 
19113 			// Allow usage of the base_uri when relative_urls = true
19114 			if (tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) {
19115 				return tu;
19116 			}
19117 
19118 			output = self.toRelPath(self.path, uri.path);
19119 
19120 			// Add query
19121 			if (uri.query) {
19122 				output += '?' + uri.query;
19123 			}
19124 
19125 			// Add anchor
19126 			if (uri.anchor) {
19127 				output += '#' + uri.anchor;
19128 			}
19129 
19130 			return output;
19131 		},
19132 
19133 		/**
19134 		 * Converts the specified URI into a absolute URI based on the current URI instance location.
19135 		 *
19136 		 * @method toAbsolute
19137 		 * @param {String} uri URI to convert into a relative path/URI.
19138 		 * @param {Boolean} noHost No host and protocol prefix.
19139 		 * @return {String} Absolute URI from the point specified in the current URI instance.
19140 		 * @example
19141 		 * // Converts an relative URL to an absolute URL url will be http://www.site.com/dir/somedir/somefile.htm
19142 		 * var url = new tinymce.util.URI('http://www.site.com/dir/').toAbsolute('somedir/somefile.htm');
19143 		 */
19144 		toAbsolute: function(uri, noHost) {
19145 			uri = new URI(uri, {base_uri: this});
19146 
19147 			return uri.getURI(noHost && this.isSameOrigin(uri));
19148 		},
19149 
19150 		/**
19151 		 * Determine whether the given URI has the same origin as this URI.  Based on RFC-6454.
19152 		 * Supports default ports for protocols listed in DEFAULT_PORTS.  Unsupported protocols will fail safe: they
19153 		 * won't match, if the port specifications differ.
19154 		 *
19155 		 * @method isSameOrigin
19156 		 * @param {tinymce.util.URI} uri Uri instance to compare.
19157 		 * @returns {Boolean} True if the origins are the same.
19158 		 */
19159 		isSameOrigin: function(uri) {
19160 			if (this.host == uri.host && this.protocol == uri.protocol){
19161 				if (this.port == uri.port) {
19162 					return true;
19163 				}
19164 
19165 				var defaultPort = DEFAULT_PORTS[this.protocol];
19166 				if (defaultPort && ((this.port || defaultPort) == (uri.port || defaultPort))) {
19167 					return true;
19168 				}
19169 			}
19170 
19171 			return false;
19172 		},
19173 
19174 		/**
19175 		 * Converts a absolute path into a relative path.
19176 		 *
19177 		 * @method toRelPath
19178 		 * @param {String} base Base point to convert the path from.
19179 		 * @param {String} path Absolute path to convert into a relative path.
19180 		 */
19181 		toRelPath: function(base, path) {
19182 			var items, breakPoint = 0, out = '', i, l;
19183 
19184 			// Split the paths
19185 			base = base.substring(0, base.lastIndexOf('/'));
19186 			base = base.split('/');
19187 			items = path.split('/');
19188 
19189 			if (base.length >= items.length) {
19190 				for (i = 0, l = base.length; i < l; i++) {
19191 					if (i >= items.length || base[i] != items[i]) {
19192 						breakPoint = i + 1;
19193 						break;
19194 					}
19195 				}
19196 			}
19197 
19198 			if (base.length < items.length) {
19199 				for (i = 0, l = items.length; i < l; i++) {
19200 					if (i >= base.length || base[i] != items[i]) {
19201 						breakPoint = i + 1;
19202 						break;
19203 					}
19204 				}
19205 			}
19206 
19207 			if (breakPoint === 1) {
19208 				return path;
19209 			}
19210 
19211 			for (i = 0, l = base.length - (breakPoint - 1); i < l; i++) {
19212 				out += "../";
19213 			}
19214 
19215 			for (i = breakPoint - 1, l = items.length; i < l; i++) {
19216 				if (i != breakPoint - 1) {
19217 					out += "/" + items[i];
19218 				} else {
19219 					out += items[i];
19220 				}
19221 			}
19222 
19223 			return out;
19224 		},
19225 
19226 		/**
19227 		 * Converts a relative path into a absolute path.
19228 		 *
19229 		 * @method toAbsPath
19230 		 * @param {String} base Base point to convert the path from.
19231 		 * @param {String} path Relative path to convert into an absolute path.
19232 		 */
19233 		toAbsPath: function(base, path) {
19234 			var i, nb = 0, o = [], tr, outPath;
19235 
19236 			// Split paths
19237 			tr = /\/$/.test(path) ? '/' : '';
19238 			base = base.split('/');
19239 			path = path.split('/');
19240 
19241 			// Remove empty chunks
19242 			each(base, function(k) {
19243 				if (k) {
19244 					o.push(k);
19245 				}
19246 			});
19247 
19248 			base = o;
19249 
19250 			// Merge relURLParts chunks
19251 			for (i = path.length - 1, o = []; i >= 0; i--) {
19252 				// Ignore empty or .
19253 				if (path[i].length === 0 || path[i] === ".") {
19254 					continue;
19255 				}
19256 
19257 				// Is parent
19258 				if (path[i] === '..') {
19259 					nb++;
19260 					continue;
19261 				}
19262 
19263 				// Move up
19264 				if (nb > 0) {
19265 					nb--;
19266 					continue;
19267 				}
19268 
19269 				o.push(path[i]);
19270 			}
19271 
19272 			i = base.length - nb;
19273 
19274 			// If /a/b/c or /
19275 			if (i <= 0) {
19276 				outPath = o.reverse().join('/');
19277 			} else {
19278 				outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/');
19279 			}
19280 
19281 			// Add front / if it's needed
19282 			if (outPath.indexOf('/') !== 0) {
19283 				outPath = '/' + outPath;
19284 			}
19285 
19286 			// Add traling / if it's needed
19287 			if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) {
19288 				outPath += tr;
19289 			}
19290 
19291 			return outPath;
19292 		},
19293 
19294 		/**
19295 		 * Returns the full URI of the internal structure.
19296 		 *
19297 		 * @method getURI
19298 		 * @param {Boolean} noProtoHost Optional no host and protocol part. Defaults to false.
19299 		 */
19300 		getURI: function(noProtoHost) {
19301 			var s, self = this;
19302 
19303 			// Rebuild source
19304 			if (!self.source || noProtoHost) {
19305 				s = '';
19306 
19307 				if (!noProtoHost) {
19308 					if (self.protocol) {
19309 						s += self.protocol + '://';
19310 					} else {
19311 						s += '//';
19312 					}
19313 
19314 					if (self.userInfo) {
19315 						s += self.userInfo + '@';
19316 					}
19317 
19318 					if (self.host) {
19319 						s += self.host;
19320 					}
19321 
19322 					if (self.port) {
19323 						s += ':' + self.port;
19324 					}
19325 				}
19326 
19327 				if (self.path) {
19328 					s += self.path;
19329 				}
19330 
19331 				if (self.query) {
19332 					s += '?' + self.query;
19333 				}
19334 
19335 				if (self.anchor) {
19336 					s += '#' + self.anchor;
19337 				}
19338 
19339 				self.source = s;
19340 			}
19341 
19342 			return self.source;
19343 		}
19344 	};
19345 
19346 	return URI;
19347 });
19348 
19349 // Included from: js/tinymce/classes/util/Class.js
19350 
19351 /**
19352  * Class.js
19353  *
19354  * Copyright 2003-2012, Moxiecode Systems AB, All rights reserved.
19355  */
19356 
19357 /**
19358  * This utilitiy class is used for easier inheritage.
19359  *
19360  * Features:
19361  * * Exposed super functions: this._super();
19362  * * Mixins
19363  * * Dummy functions
19364  * * Property functions: var value = object.value(); and object.value(newValue);
19365  * * Static functions
19366  * * Defaults settings
19367  */
19368 define("tinymce/util/Class", [
19369 	"tinymce/util/Tools"
19370 ], function(Tools) {
19371 	var each = Tools.each, extend = Tools.extend;
19372 
19373 	var extendClass, initializing;
19374 
19375 	function Class() {
19376 	}
19377 
19378 	// Provides classical inheritance, based on code made by John Resig
19379 	Class.extend = extendClass = function(prop) {
19380 		var self = this, _super = self.prototype, prototype, name, member;
19381 
19382 		// The dummy class constructor
19383 		function Class() {
19384 			var i, mixins, mixin, self = this;
19385 
19386 			// All construction is actually done in the init method
19387 			if (!initializing) {
19388 				// Run class constuctor
19389 				if (self.init) {
19390 					self.init.apply(self, arguments);
19391 				}
19392 
19393 				// Run mixin constructors
19394 				mixins = self.Mixins;
19395 				if (mixins) {
19396 					i = mixins.length;
19397 					while (i--) {
19398 						mixin = mixins[i];
19399 						if (mixin.init) {
19400 							mixin.init.apply(self, arguments);
19401 						}
19402 					}
19403 				}
19404 			}
19405 		}
19406 
19407 		// Dummy function, needs to be extended in order to provide functionality
19408 		function dummy() {
19409 			return this;
19410 		}
19411 
19412 		// Creates a overloaded method for the class
19413 		// this enables you to use this._super(); to call the super function
19414 		function createMethod(name, fn) {
19415 			return function(){
19416 				var self = this, tmp = self._super, ret;
19417 
19418 				self._super = _super[name];
19419 				ret = fn.apply(self, arguments);
19420 				self._super = tmp;
19421 
19422 				return ret;
19423 			};
19424 		}
19425 
19426 		// Instantiate a base class (but only create the instance,
19427 		// don't run the init constructor)
19428 		initializing = true;
19429 		
19430 		/*eslint new-cap:0 */
19431 		prototype = new self();
19432 		initializing = false;
19433 
19434 		// Add mixins
19435 		if (prop.Mixins) {
19436 			each(prop.Mixins, function(mixin) {
19437 				mixin = mixin;
19438 
19439 				for (var name in mixin) {
19440 					if (name !== "init") {
19441 						prop[name] = mixin[name];
19442 					}
19443 				}
19444 			});
19445 
19446 			if (_super.Mixins) {
19447 				prop.Mixins = _super.Mixins.concat(prop.Mixins);
19448 			}
19449 		}
19450 
19451 		// Generate dummy methods
19452 		if (prop.Methods) {
19453 			each(prop.Methods.split(','), function(name) {
19454 				prop[name] = dummy;
19455 			});
19456 		}
19457 
19458 		// Generate property methods
19459 		if (prop.Properties) {
19460 			each(prop.Properties.split(','), function(name) {
19461 				var fieldName = '_' + name;
19462 
19463 				prop[name] = function(value) {
19464 					var self = this, undef;
19465 
19466 					// Set value
19467 					if (value !== undef) {
19468 						self[fieldName] = value;
19469 
19470 						return self;
19471 					}
19472 
19473 					// Get value
19474 					return self[fieldName];
19475 				};
19476 			});
19477 		}
19478 
19479 		// Static functions
19480 		if (prop.Statics) {
19481 			each(prop.Statics, function(func, name) {
19482 				Class[name] = func;
19483 			});
19484 		}
19485 
19486 		// Default settings
19487 		if (prop.Defaults && _super.Defaults) {
19488 			prop.Defaults = extend({}, _super.Defaults, prop.Defaults);
19489 		}
19490 
19491 		// Copy the properties over onto the new prototype
19492 		for (name in prop) {
19493 			member = prop[name];
19494 
19495 			if (typeof member == "function" && _super[name]) {
19496 				prototype[name] = createMethod(name, member);
19497 			} else {
19498 				prototype[name] = member;
19499 			}
19500 		}
19501 
19502 		// Populate our constructed prototype object
19503 		Class.prototype = prototype;
19504 
19505 		// Enforce the constructor to be what we expect
19506 		Class.constructor = Class;
19507 
19508 		// And make this class extendible
19509 		Class.extend = extendClass;
19510 
19511 		return Class;
19512 	};
19513 
19514 	return Class;
19515 });
19516 
19517 // Included from: js/tinymce/classes/util/EventDispatcher.js
19518 
19519 /**
19520  * EventDispatcher.js
19521  *
19522  * Copyright, Moxiecode Systems AB
19523  * Released under LGPL License.
19524  *
19525  * License: http://www.tinymce.com/license
19526  * Contributing: http://www.tinymce.com/contributing
19527  */
19528 
19529 /**
19530  * This class lets you add/remove and fire events by name on the specified scope. This makes
19531  * it easy to add event listener logic to any class.
19532  *
19533  * @class tinymce.util.EventDispatcher
19534  * @example
19535  *  var eventDispatcher = new EventDispatcher();
19536  *
19537  *  eventDispatcher.on('click', function() {console.log('data');});
19538  *  eventDispatcher.fire('click', {data: 123});
19539  */
19540 define("tinymce/util/EventDispatcher", [
19541 	"tinymce/util/Tools"
19542 ], function(Tools) {
19543 	var nativeEvents = Tools.makeMap(
19544 		"focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " +
19545 		"mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " +
19546 		"draggesture dragdrop drop drag submit",
19547 		' '
19548 	);
19549 
19550 	function Dispatcher(settings) {
19551 		var self = this, scope, bindings = {}, toggleEvent;
19552 
19553 		function returnFalse() {
19554 			return false;
19555 		}
19556 
19557 		function returnTrue() {
19558 			return true;
19559 		}
19560 
19561 		settings = settings || {};
19562 		scope = settings.scope || self;
19563 		toggleEvent = settings.toggleEvent || returnFalse;
19564 
19565 		/**
19566 		 * Fires the specified event by name.
19567 		 *
19568 		 * @method fire
19569 		 * @param {String} name Name of the event to fire.
19570 		 * @param {Object?} args Event arguments.
19571 		 * @return {Object} Event args instance passed in.
19572 		 * @example
19573 		 * instance.fire('event', {...});
19574 		 */
19575 		function fire(name, args) {
19576 			var handlers, i, l, callback;
19577 
19578 			name = name.toLowerCase();
19579 			args = args || {};
19580 			args.type = name;
19581 
19582 			// Setup target is there isn't one
19583 			if (!args.target) {
19584 				args.target = scope;
19585 			}
19586 
19587 			// Add event delegation methods if they are missing
19588 			if (!args.preventDefault) {
19589 				// Add preventDefault method
19590 				args.preventDefault = function() {
19591 					args.isDefaultPrevented = returnTrue;
19592 				};
19593 
19594 				// Add stopPropagation
19595 				args.stopPropagation = function() {
19596 					args.isPropagationStopped = returnTrue;
19597 				};
19598 
19599 				// Add stopImmediatePropagation
19600 				args.stopImmediatePropagation = function() {
19601 					args.isImmediatePropagationStopped = returnTrue;
19602 				};
19603 
19604 				// Add event delegation states
19605 				args.isDefaultPrevented = returnFalse;
19606 				args.isPropagationStopped = returnFalse;
19607 				args.isImmediatePropagationStopped = returnFalse;
19608 			}
19609 
19610 			if (settings.beforeFire) {
19611 				settings.beforeFire(args);
19612 			}
19613 
19614 			handlers = bindings[name];
19615 			if (handlers) {
19616 				for (i = 0, l = handlers.length; i < l; i++) {
19617 					handlers[i] = callback = handlers[i];
19618 
19619 					// Stop immediate propagation if needed
19620 					if (args.isImmediatePropagationStopped()) {
19621 						args.stopPropagation();
19622 						return args;
19623 					}
19624 
19625 					// If callback returns false then prevent default and stop all propagation
19626 					if (callback.call(scope, args) === false) {
19627 						args.preventDefault();
19628 						return args;
19629 					}
19630 				}
19631 			}
19632 
19633 			return args;
19634 		}
19635 
19636 		/**
19637 		 * Binds an event listener to a specific event by name.
19638 		 *
19639 		 * @method on
19640 		 * @param {String} name Event name or space separated list of events to bind.
19641 		 * @param {callback} callback Callback to be executed when the event occurs.
19642 		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
19643 		 * @return {Object} Current class instance.
19644 		 * @example
19645 		 * instance.on('event', function(e) {
19646 		 *     // Callback logic
19647 		 * });
19648 		 */
19649 		function on(name, callback, prepend) {
19650 			var handlers, names, i;
19651 
19652 			if (callback === false) {
19653 				callback = returnFalse;
19654 			}
19655 
19656 			if (callback) {
19657 				names = name.toLowerCase().split(' ');
19658 				i = names.length;
19659 				while (i--) {
19660 					name = names[i];
19661 					handlers = bindings[name];
19662 					if (!handlers) {
19663 						handlers = bindings[name] = [];
19664 						toggleEvent(name, true);
19665 					}
19666 
19667 					if (prepend) {
19668 						handlers.unshift(callback);
19669 					} else {
19670 						handlers.push(callback);
19671 					}
19672 				}
19673 			}
19674 
19675 			return self;
19676 		}
19677 
19678 		/**
19679 		 * Unbinds an event listener to a specific event by name.
19680 		 *
19681 		 * @method off
19682 		 * @param {String?} name Name of the event to unbind.
19683 		 * @param {callback?} callback Callback to unbind.
19684 		 * @return {Object} Current class instance.
19685 		 * @example
19686 		 * // Unbind specific callback
19687 		 * instance.off('event', handler);
19688 		 *
19689 		 * // Unbind all listeners by name
19690 		 * instance.off('event');
19691 		 *
19692 		 * // Unbind all events
19693 		 * instance.off();
19694 		 */
19695 		function off(name, callback) {
19696 			var i, handlers, bindingName, names, hi;
19697 
19698 			if (name) {
19699 				names = name.toLowerCase().split(' ');
19700 				i = names.length;
19701 				while (i--) {
19702 					name = names[i];
19703 					handlers = bindings[name];
19704 
19705 					// Unbind all handlers
19706 					if (!name) {
19707 						for (bindingName in bindings) {
19708 							toggleEvent(bindingName, false);
19709 							delete bindings[bindingName];
19710 						}
19711 
19712 						return self;
19713 					}
19714 
19715 					if (handlers) {
19716 						// Unbind all by name
19717 						if (!callback) {
19718 							handlers.length = 0;
19719 						} else {
19720 							// Unbind specific ones
19721 							hi = handlers.length;
19722 							while (hi--) {
19723 								if (handlers[hi] === callback) {
19724 									handlers.splice(hi, 1);
19725 								}
19726 							}
19727 						}
19728 
19729 						if (!handlers.length) {
19730 							toggleEvent(name, false);
19731 							delete bindings[name];
19732 						}
19733 					}
19734 				}
19735 			} else {
19736 				for (name in bindings) {
19737 					toggleEvent(name, false);
19738 				}
19739 
19740 				bindings = {};
19741 			}
19742 
19743 			return self;
19744 		}
19745 
19746 		/**
19747 		 * Returns true/false if the dispatcher has a event of the specified name.
19748 		 *
19749 		 * @method has
19750 		 * @param {String} name Name of the event to check for.
19751 		 * @return {Boolean} true/false if the event exists or not.
19752 		 */
19753 		function has(name) {
19754 			name = name.toLowerCase();
19755 			return !(!bindings[name] || bindings[name].length === 0);
19756 		}
19757 
19758 		// Expose
19759 		self.fire = fire;
19760 		self.on = on;
19761 		self.off = off;
19762 		self.has = has;
19763 	}
19764 
19765 	/**
19766 	 * Returns true/false if the specified event name is a native browser event or not.
19767 	 *
19768 	 * @method isNative
19769 	 * @param {String} name Name to check if it's native.
19770 	 * @return {Boolean} true/false if the event is native or not.
19771 	 * @static
19772 	 */
19773 	Dispatcher.isNative = function(name) {
19774 		return !!nativeEvents[name.toLowerCase()];
19775 	};
19776 
19777 	return Dispatcher;
19778 });
19779 
19780 // Included from: js/tinymce/classes/ui/Selector.js
19781 
19782 /**
19783  * Selector.js
19784  *
19785  * Copyright, Moxiecode Systems AB
19786  * Released under LGPL License.
19787  *
19788  * License: http://www.tinymce.com/license
19789  * Contributing: http://www.tinymce.com/contributing
19790  */
19791 
19792 /*eslint no-nested-ternary:0 */
19793 
19794 /**
19795  * Selector engine, enables you to select controls by using CSS like expressions.
19796  * We currently only support basic CSS expressions to reduce the size of the core
19797  * and the ones we support should be enough for most cases.
19798  *
19799  * @example
19800  * Supported expressions:
19801  *  element
19802  *  element#name
19803  *  element.class
19804  *  element[attr]
19805  *  element[attr*=value]
19806  *  element[attr~=value]
19807  *  element[attr!=value]
19808  *  element[attr^=value]
19809  *  element[attr$=value]
19810  *  element:<state>
19811  *  element:not(<expression>)
19812  *  element:first
19813  *  element:last
19814  *  element:odd
19815  *  element:even
19816  *  element element
19817  *  element > element
19818  *
19819  * @class tinymce.ui.Selector
19820  */
19821 define("tinymce/ui/Selector", [
19822 	"tinymce/util/Class"
19823 ], function(Class) {
19824 	"use strict";
19825 
19826 	/**
19827 	 * Produces an array with a unique set of objects. It will not compare the values
19828 	 * but the references of the objects.
19829 	 *
19830 	 * @private
19831 	 * @method unqiue
19832 	 * @param {Array} array Array to make into an array with unique items.
19833 	 * @return {Array} Array with unique items.
19834 	 */
19835 	function unique(array) {
19836 		var uniqueItems = [], i = array.length, item;
19837 
19838 		while (i--) {
19839 			item = array[i];
19840 
19841 			if (!item.__checked) {
19842 				uniqueItems.push(item);
19843 				item.__checked = 1;
19844 			}
19845 		}
19846 
19847 		i = uniqueItems.length;
19848 		while (i--) {
19849 			delete uniqueItems[i].__checked;
19850 		}
19851 
19852 		return uniqueItems;
19853 	}
19854 
19855 	var expression = /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i;
19856 
19857 	/*jshint maxlen:255 */
19858 	/*eslint max-len:0 */
19859 	var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
19860 		whiteSpace = /^\s*|\s*$/g,
19861 		Collection;
19862 
19863 	var Selector = Class.extend({
19864 		/**
19865 		 * Constructs a new Selector instance.
19866 		 *
19867 		 * @constructor
19868 		 * @method init
19869 		 * @param {String} selector CSS like selector expression.
19870 		 */
19871 		init: function(selector) {
19872 			var match = this.match;
19873 
19874 			function compileNameFilter(name) {
19875 				if (name) {
19876 					name = name.toLowerCase();
19877 
19878 					return function(item) {
19879 						return name === '*' || item.type === name;
19880 					};
19881 				}
19882 			}
19883 
19884 			function compileIdFilter(id) {
19885 				if (id) {
19886 					return function(item) {
19887 						return item._name === id;
19888 					};
19889 				}
19890 			}
19891 
19892 			function compileClassesFilter(classes) {
19893 				if (classes) {
19894 					classes = classes.split('.');
19895 
19896 					return function(item) {
19897 						var i = classes.length;
19898 
19899 						while (i--) {
19900 							if (!item.hasClass(classes[i])) {
19901 								return false;
19902 							}
19903 						}
19904 
19905 						return true;
19906 					};
19907 				}
19908 			}
19909 
19910 			function compileAttrFilter(name, cmp, check) {
19911 				if (name) {
19912 					return function(item) {
19913 						var value = item[name] ? item[name]() : '';
19914 
19915 						return !cmp ? !!check :
19916 							cmp === "=" ? value === check :
19917 							cmp === "*=" ? value.indexOf(check) >= 0 :
19918 							cmp === "~=" ? (" " + value + " ").indexOf(" " + check + " ") >= 0 :
19919 							cmp === "!=" ? value != check :
19920 							cmp === "^=" ? value.indexOf(check) === 0 :
19921 							cmp === "$=" ? value.substr(value.length - check.length) === check :
19922 							false;
19923 					};
19924 				}
19925 			}
19926 
19927 			function compilePsuedoFilter(name) {
19928 				var notSelectors;
19929 
19930 				if (name) {
19931 					name = /(?:not\((.+)\))|(.+)/i.exec(name);
19932 
19933 					if (!name[1]) {
19934 						name = name[2];
19935 
19936 						return function(item, index, length) {
19937 							return name === 'first' ? index === 0 :
19938 								name === 'last' ? index === length - 1 :
19939 								name === 'even' ? index % 2 === 0 :
19940 								name === 'odd' ? index % 2 === 1 :
19941 								item[name] ? item[name]() :
19942 								false;
19943 						};
19944 					} else {
19945 						// Compile not expression
19946 						notSelectors = parseChunks(name[1], []);
19947 
19948 						return function(item) {
19949 							return !match(item, notSelectors);
19950 						};
19951 					}
19952 				}
19953 			}
19954 
19955 			function compile(selector, filters, direct) {
19956 				var parts;
19957 
19958 				function add(filter) {
19959 					if (filter) {
19960 						filters.push(filter);
19961 					}
19962 				}
19963 
19964 				// Parse expression into parts
19965 				parts = expression.exec(selector.replace(whiteSpace, ''));
19966 
19967 				add(compileNameFilter(parts[1]));
19968 				add(compileIdFilter(parts[2]));
19969 				add(compileClassesFilter(parts[3]));
19970 				add(compileAttrFilter(parts[4], parts[5], parts[6]));
19971 				add(compilePsuedoFilter(parts[7]));
19972 
19973 				// Mark the filter with psuedo for performance
19974 				filters.psuedo = !!parts[7];
19975 				filters.direct = direct;
19976 
19977 				return filters;
19978 			}
19979 
19980 			// Parser logic based on Sizzle by John Resig
19981 			function parseChunks(selector, selectors) {
19982 				var parts = [], extra, matches, i;
19983 
19984 				do {
19985 					chunker.exec("");
19986 					matches = chunker.exec(selector);
19987 
19988 					if (matches) {
19989 						selector = matches[3];
19990 						parts.push(matches[1]);
19991 
19992 						if (matches[2]) {
19993 							extra = matches[3];
19994 							break;
19995 						}
19996 					}
19997 				} while (matches);
19998 
19999 				if (extra) {
20000 					parseChunks(extra, selectors);
20001 				}
20002 
20003 				selector = [];
20004 				for (i = 0; i < parts.length; i++) {
20005 					if (parts[i] != '>') {
20006 						selector.push(compile(parts[i], [], parts[i - 1] === '>'));
20007 					}
20008 				}
20009 
20010 				selectors.push(selector);
20011 
20012 				return selectors;
20013 			}
20014 
20015 			this._selectors = parseChunks(selector, []);
20016 		},
20017 
20018 		/**
20019 		 * Returns true/false if the selector matches the specified control.
20020 		 *
20021 		 * @method match
20022 		 * @param {tinymce.ui.Control} control Control to match agains the selector.
20023 		 * @param {Array} selectors Optional array of selectors, mostly used internally.
20024 		 * @return {Boolean} true/false state if the control matches or not.
20025 		 */
20026 		match: function(control, selectors) {
20027 			var i, l, si, sl, selector, fi, fl, filters, index, length, siblings, count, item;
20028 
20029 			selectors = selectors || this._selectors;
20030 			for (i = 0, l = selectors.length; i < l; i++) {
20031 				selector = selectors[i];
20032 				sl = selector.length;
20033 				item = control;
20034 				count = 0;
20035 
20036 				for (si = sl - 1; si >= 0; si--) {
20037 					filters = selector[si];
20038 
20039 					while (item) {
20040 						// Find the index and length since a psuedo filter like :first needs it
20041 						if (filters.psuedo) {
20042 							siblings = item.parent().items();
20043 							index = length = siblings.length;
20044 							while (index--) {
20045 								if (siblings[index] === item) {
20046 									break;
20047 								}
20048 							}
20049 						}
20050 
20051 						for (fi = 0, fl = filters.length; fi < fl; fi++) {
20052 							if (!filters[fi](item, index, length)) {
20053 								fi = fl + 1;
20054 								break;
20055 							}
20056 						}
20057 
20058 						if (fi === fl) {
20059 							count++;
20060 							break;
20061 						} else {
20062 							// If it didn't match the right most expression then
20063 							// break since it's no point looking at the parents
20064 							if (si === sl - 1) {
20065 								break;
20066 							}
20067 						}
20068 
20069 						item = item.parent();
20070 					}
20071 				}
20072 
20073 				// If we found all selectors then return true otherwise continue looking
20074 				if (count === sl) {
20075 					return true;
20076 				}
20077 			}
20078 
20079 			return false;
20080 		},
20081 
20082 		/**
20083 		 * Returns a tinymce.ui.Collection with matches of the specified selector inside the specified container.
20084 		 *
20085 		 * @method find
20086 		 * @param {tinymce.ui.Control} container Container to look for items in.
20087 		 * @return {tinymce.ui.Collection} Collection with matched elements.
20088 		 */
20089 		find: function(container) {
20090 			var matches = [], i, l, selectors = this._selectors;
20091 
20092 			function collect(items, selector, index) {
20093 				var i, l, fi, fl, item, filters = selector[index];
20094 
20095 				for (i = 0, l = items.length; i < l; i++) {
20096 					item = items[i];
20097 
20098 					// Run each filter agains the item
20099 					for (fi = 0, fl = filters.length; fi < fl; fi++) {
20100 						if (!filters[fi](item, i, l)) {
20101 							fi = fl + 1;
20102 							break;
20103 						}
20104 					}
20105 
20106 					// All filters matched the item
20107 					if (fi === fl) {
20108 						// Matched item is on the last expression like: panel toolbar [button]
20109 						if (index == selector.length - 1) {
20110 							matches.push(item);
20111 						} else {
20112 							// Collect next expression type
20113 							if (item.items) {
20114 								collect(item.items(), selector, index + 1);
20115 							}
20116 						}
20117 					} else if (filters.direct) {
20118 						return;
20119 					}
20120 
20121 					// Collect child items
20122 					if (item.items) {
20123 						collect(item.items(), selector, index);
20124 					}
20125 				}
20126 			}
20127 
20128 			if (container.items) {
20129 				for (i = 0, l = selectors.length; i < l; i++) {
20130 					collect(container.items(), selectors[i], 0);
20131 				}
20132 
20133 				// Unique the matches if needed
20134 				if (l > 1) {
20135 					matches = unique(matches);
20136 				}
20137 			}
20138 
20139 			// Fix for circular reference
20140 			if (!Collection) {
20141 				// TODO: Fix me!
20142 				Collection = Selector.Collection;
20143 			}
20144 
20145 			return new Collection(matches);
20146 		}
20147 	});
20148 
20149 	return Selector;
20150 });
20151 
20152 // Included from: js/tinymce/classes/ui/Collection.js
20153 
20154 /**
20155  * Collection.js
20156  *
20157  * Copyright, Moxiecode Systems AB
20158  * Released under LGPL License.
20159  *
20160  * License: http://www.tinymce.com/license
20161  * Contributing: http://www.tinymce.com/contributing
20162  */
20163 
20164 /**
20165  * Control collection, this class contains control instances and it enables you to
20166  * perform actions on all the contained items. This is very similar to how jQuery works.
20167  *
20168  * @example
20169  * someCollection.show().disabled(true);
20170  *
20171  * @class tinymce.ui.Collection
20172  */
20173 define("tinymce/ui/Collection", [
20174 	"tinymce/util/Tools",
20175 	"tinymce/ui/Selector",
20176 	"tinymce/util/Class"
20177 ], function(Tools, Selector, Class) {
20178 	"use strict";
20179 
20180 	var Collection, proto, push = Array.prototype.push, slice = Array.prototype.slice;
20181 
20182 	proto = {
20183 		/**
20184 		 * Current number of contained control instances.
20185 		 *
20186 		 * @field length
20187 		 * @type Number
20188 		 */
20189 		length: 0,
20190 
20191 		/**
20192 		 * Constructor for the collection.
20193 		 *
20194 		 * @constructor
20195 		 * @method init
20196 		 * @param {Array} items Optional array with items to add.
20197 		 */
20198 		init: function(items) {
20199 			if (items) {
20200 				this.add(items);
20201 			}
20202 		},
20203 
20204 		/**
20205 		 * Adds new items to the control collection.
20206 		 *
20207 		 * @method add
20208 		 * @param {Array} items Array if items to add to collection.
20209 		 * @return {tinymce.ui.Collection} Current collection instance.
20210 		 */
20211 		add: function(items) {
20212 			var self = this;
20213 
20214 			// Force single item into array
20215 			if (!Tools.isArray(items)) {
20216 				if (items instanceof Collection) {
20217 					self.add(items.toArray());
20218 				} else {
20219 					push.call(self, items);
20220 				}
20221 			} else {
20222 				push.apply(self, items);
20223 			}
20224 
20225 			return self;
20226 		},
20227 
20228 		/**
20229 		 * Sets the contents of the collection. This will remove any existing items
20230 		 * and replace them with the ones specified in the input array.
20231 		 *
20232 		 * @method set
20233 		 * @param {Array} items Array with items to set into the Collection.
20234 		 * @return {tinymce.ui.Collection} Collection instance.
20235 		 */
20236 		set: function(items) {
20237 			var self = this, len = self.length, i;
20238 
20239 			self.length = 0;
20240 			self.add(items);
20241 
20242 			// Remove old entries
20243 			for (i = self.length; i < len; i++) {
20244 				delete self[i];
20245 			}
20246 
20247 			return self;
20248 		},
20249 
20250 		/**
20251 		 * Filters the collection item based on the specified selector expression or selector function.
20252 		 *
20253 		 * @method filter
20254 		 * @param {String} selector Selector expression to filter items by.
20255 		 * @return {tinymce.ui.Collection} Collection containing the filtered items.
20256 		 */
20257 		filter: function(selector) {
20258 			var self = this, i, l, matches = [], item, match;
20259 
20260 			// Compile string into selector expression
20261 			if (typeof(selector) === "string") {
20262 				selector = new Selector(selector);
20263 
20264 				match = function(item) {
20265 					return selector.match(item);
20266 				};
20267 			} else {
20268 				// Use selector as matching function
20269 				match = selector;
20270 			}
20271 
20272 			for (i = 0, l = self.length; i < l; i++) {
20273 				item = self[i];
20274 
20275 				if (match(item)) {
20276 					matches.push(item);
20277 				}
20278 			}
20279 
20280 			return new Collection(matches);
20281 		},
20282 
20283 		/**
20284 		 * Slices the items within the collection.
20285 		 *
20286 		 * @method slice
20287 		 * @param {Number} index Index to slice at.
20288 		 * @param {Number} len Optional length to slice.
20289 		 * @return {tinymce.ui.Collection} Current collection.
20290 		 */
20291 		slice: function() {
20292 			return new Collection(slice.apply(this, arguments));
20293 		},
20294 
20295 		/**
20296 		 * Makes the current collection equal to the specified index.
20297 		 *
20298 		 * @method eq
20299 		 * @param {Number} index Index of the item to set the collection to.
20300 		 * @return {tinymce.ui.Collection} Current collection.
20301 		 */
20302 		eq: function(index) {
20303 			return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
20304 		},
20305 
20306 		/**
20307 		 * Executes the specified callback on each item in collection.
20308 		 *
20309 		 * @method each
20310 		 * @param {function} callback Callback to execute for each item in collection.
20311 		 * @return {tinymce.ui.Collection} Current collection instance.
20312 		 */
20313 		each: function(callback) {
20314 			Tools.each(this, callback);
20315 
20316 			return this;
20317 		},
20318 
20319 		/**
20320 		 * Returns an JavaScript array object of the contents inside the collection.
20321 		 *
20322 		 * @method toArray
20323 		 * @return {Array} Array with all items from collection.
20324 		 */
20325 		toArray: function() {
20326 			return Tools.toArray(this);
20327 		},
20328 
20329 		/**
20330 		 * Finds the index of the specified control or return -1 if it isn't in the collection.
20331 		 *
20332 		 * @method indexOf
20333 		 * @param {Control} ctrl Control instance to look for.
20334 		 * @return {Number} Index of the specified control or -1.
20335 		 */
20336 		indexOf: function(ctrl) {
20337 			var self = this, i = self.length;
20338 
20339 			while (i--) {
20340 				if (self[i] === ctrl) {
20341 					break;
20342 				}
20343 			}
20344 
20345 			return i;
20346 		},
20347 
20348 		/**
20349 		 * Returns a new collection of the contents in reverse order.
20350 		 *
20351 		 * @method reverse
20352 		 * @return {tinymce.ui.Collection} Collection instance with reversed items.
20353 		 */
20354 		reverse: function() {
20355 			return new Collection(Tools.toArray(this).reverse());
20356 		},
20357 
20358 		/**
20359 		 * Returns true/false if the class exists or not.
20360 		 *
20361 		 * @method hasClass
20362 		 * @param {String} cls Class to check for.
20363 		 * @return {Boolean} true/false state if the class exists or not.
20364 		 */
20365 		hasClass: function(cls) {
20366 			return this[0] ? this[0].hasClass(cls) : false;
20367 		},
20368 
20369 		/**
20370 		 * Sets/gets the specific property on the items in the collection. The same as executing control.<property>(<value>);
20371 		 *
20372 		 * @method prop
20373 		 * @param {String} name Property name to get/set.
20374 		 * @param {Object} value Optional object value to set.
20375 		 * @return {tinymce.ui.Collection} Current collection instance or value of the first item on a get operation.
20376 		 */
20377 		prop: function(name, value) {
20378 			var self = this, undef, item;
20379 
20380 			if (value !== undef) {
20381 				self.each(function(item) {
20382 					if (item[name]) {
20383 						item[name](value);
20384 					}
20385 				});
20386 
20387 				return self;
20388 			}
20389 
20390 			item = self[0];
20391 
20392 			if (item && item[name]) {
20393 				return item[name]();
20394 			}
20395 		},
20396 
20397 		/**
20398 		 * Executes the specific function name with optional arguments an all items in collection if it exists.
20399 		 *
20400 		 * @example collection.exec("myMethod", arg1, arg2, arg3);
20401 		 * @method exec
20402 		 * @param {String} name Name of the function to execute.
20403 		 * @param {Object} ... Multiple arguments to pass to each function.
20404 		 * @return {tinymce.ui.Collection} Current collection.
20405 		 */
20406 		exec: function(name) {
20407 			var self = this, args = Tools.toArray(arguments).slice(1);
20408 
20409 			self.each(function(item) {
20410 				if (item[name]) {
20411 					item[name].apply(item, args);
20412 				}
20413 			});
20414 
20415 			return self;
20416 		},
20417 
20418 		/**
20419 		 * Remove all items from collection and DOM.
20420 		 *
20421 		 * @method remove
20422 		 * @return {tinymce.ui.Collection} Current collection.
20423 		 */
20424 		remove: function() {
20425 			var i = this.length;
20426 
20427 			while (i--) {
20428 				this[i].remove();
20429 			}
20430 
20431 			return this;
20432 		}
20433 
20434 		/**
20435 		 * Fires the specified event by name and arguments on the control. This will execute all
20436 		 * bound event handlers.
20437 		 *
20438 		 * @method fire
20439 		 * @param {String} name Name of the event to fire.
20440 		 * @param {Object} args Optional arguments to pass to the event.
20441 		 * @return {tinymce.ui.Collection} Current collection instance.
20442 		 */
20443 		// fire: function(event, args) {}, -- Generated by code below
20444 
20445 		/**
20446 		 * Binds a callback to the specified event. This event can both be
20447 		 * native browser events like "click" or custom ones like PostRender.
20448 		 *
20449 		 * The callback function will have two parameters the first one being the control that received the event
20450 		 * the second one will be the event object either the browsers native event object or a custom JS object.
20451 		 *
20452 		 * @method on
20453 		 * @param {String} name Name of the event to bind. For example "click".
20454 		 * @param {String/function} callback Callback function to execute ones the event occurs.
20455 		 * @return {tinymce.ui.Collection} Current collection instance.
20456 		 */
20457 		// on: function(name, callback) {}, -- Generated by code below
20458 
20459 		/**
20460 		 * Unbinds the specified event and optionally a specific callback. If you omit the name
20461 		 * parameter all event handlers will be removed. If you omit the callback all event handles
20462 		 * by the specified name will be removed.
20463 		 *
20464 		 * @method off
20465 		 * @param {String} name Optional name for the event to unbind.
20466 		 * @param {function} callback Optional callback function to unbind.
20467 		 * @return {tinymce.ui.Collection} Current collection instance.
20468 		 */
20469 		// off: function(name, callback) {}, -- Generated by code below
20470 
20471 		/**
20472 		 * Shows the items in the current collection.
20473 		 *
20474 		 * @method show
20475 		 * @return {tinymce.ui.Collection} Current collection instance.
20476 		 */
20477 		// show: function() {}, -- Generated by code below
20478 
20479 		/**
20480 		 * Hides the items in the current collection.
20481 		 *
20482 		 * @method hide
20483 		 * @return {tinymce.ui.Collection} Current collection instance.
20484 		 */
20485 		// hide: function() {}, -- Generated by code below
20486 
20487 		/**
20488 		 * Sets/gets the text contents of the items in the current collection.
20489 		 *
20490 		 * @method text
20491 		 * @return {tinymce.ui.Collection} Current collection instance or text value of the first item on a get operation.
20492 		 */
20493 		// text: function(value) {}, -- Generated by code below
20494 
20495 		/**
20496 		 * Sets/gets the name contents of the items in the current collection.
20497 		 *
20498 		 * @method name
20499 		 * @return {tinymce.ui.Collection} Current collection instance or name value of the first item on a get operation.
20500 		 */
20501 		// name: function(value) {}, -- Generated by code below
20502 
20503 		/**
20504 		 * Sets/gets the disabled state on the items in the current collection.
20505 		 *
20506 		 * @method disabled
20507 		 * @return {tinymce.ui.Collection} Current collection instance or disabled state of the first item on a get operation.
20508 		 */
20509 		// disabled: function(state) {}, -- Generated by code below
20510 
20511 		/**
20512 		 * Sets/gets the active state on the items in the current collection.
20513 		 *
20514 		 * @method active
20515 		 * @return {tinymce.ui.Collection} Current collection instance or active state of the first item on a get operation.
20516 		 */
20517 		// active: function(state) {}, -- Generated by code below
20518 
20519 		/**
20520 		 * Sets/gets the selected state on the items in the current collection.
20521 		 *
20522 		 * @method selected
20523 		 * @return {tinymce.ui.Collection} Current collection instance or selected state of the first item on a get operation.
20524 		 */
20525 		// selected: function(state) {}, -- Generated by code below
20526 
20527 		/**
20528 		 * Sets/gets the selected state on the items in the current collection.
20529 		 *
20530 		 * @method visible
20531 		 * @return {tinymce.ui.Collection} Current collection instance or visible state of the first item on a get operation.
20532 		 */
20533 		// visible: function(state) {}, -- Generated by code below
20534 
20535 		/**
20536 		 * Adds a class to all items in the collection.
20537 		 *
20538 		 * @method addClass
20539 		 * @param {String} cls Class to add to each item.
20540 		 * @return {tinymce.ui.Collection} Current collection instance.
20541 		 */
20542 		// addClass: function(cls) {}, -- Generated by code below
20543 
20544 		/**
20545 		 * Removes the specified class from all items in collection.
20546 		 *
20547 		 * @method removeClass
20548 		 * @param {String} cls Class to remove from each item.
20549 		 * @return {tinymce.ui.Collection} Current collection instance.
20550 		 */
20551 		// removeClass: function(cls) {}, -- Generated by code below
20552 	};
20553 
20554 	// Extend tinymce.ui.Collection prototype with some generated control specific methods
20555 	Tools.each('fire on off show hide addClass removeClass append prepend before after reflow'.split(' '), function(name) {
20556 		proto[name] = function() {
20557 			var args = Tools.toArray(arguments);
20558 
20559 			this.each(function(ctrl) {
20560 				if (name in ctrl) {
20561 					ctrl[name].apply(ctrl, args);
20562 				}
20563 			});
20564 
20565 			return this;
20566 		};
20567 	});
20568 
20569 	// Extend tinymce.ui.Collection prototype with some property methods
20570 	Tools.each('text name disabled active selected checked visible parent value data'.split(' '), function(name) {
20571 		proto[name] = function(value) {
20572 			return this.prop(name, value);
20573 		};
20574 	});
20575 
20576 	// Create class based on the new prototype
20577 	Collection = Class.extend(proto);
20578 
20579 	// Stick Collection into Selector to prevent circual references
20580 	Selector.Collection = Collection;
20581 
20582 	return Collection;
20583 });
20584 
20585 // Included from: js/tinymce/classes/ui/DomUtils.js
20586 
20587 /**
20588  * DOMUtils.js
20589  *
20590  * Copyright, Moxiecode Systems AB
20591  * Released under LGPL License.
20592  *
20593  * License: http://www.tinymce.com/license
20594  * Contributing: http://www.tinymce.com/contributing
20595  */
20596 
20597 define("tinymce/ui/DomUtils", [
20598 	"tinymce/util/Tools",
20599 	"tinymce/dom/DOMUtils"
20600 ], function(Tools, DOMUtils) {
20601 	"use strict";
20602 
20603 	return {
20604 		id: function() {
20605 			return DOMUtils.DOM.uniqueId();
20606 		},
20607 
20608 		createFragment: function(html) {
20609 			return DOMUtils.DOM.createFragment(html);
20610 		},
20611 
20612 		getWindowSize: function() {
20613 			return DOMUtils.DOM.getViewPort();
20614 		},
20615 
20616 		getSize: function(elm) {
20617 			var width, height;
20618 
20619 			if (elm.getBoundingClientRect) {
20620 				var rect = elm.getBoundingClientRect();
20621 
20622 				width = Math.max(rect.width || (rect.right - rect.left), elm.offsetWidth);
20623 				height = Math.max(rect.height || (rect.bottom - rect.bottom), elm.offsetHeight);
20624 			} else {
20625 				width = elm.offsetWidth;
20626 				height = elm.offsetHeight;
20627 			}
20628 
20629 			return {width: width, height: height};
20630 		},
20631 
20632 		getPos: function(elm, root) {
20633 			return DOMUtils.DOM.getPos(elm, root);
20634 		},
20635 
20636 		getViewPort: function(win) {
20637 			return DOMUtils.DOM.getViewPort(win);
20638 		},
20639 
20640 		get: function(id) {
20641 			return document.getElementById(id);
20642 		},
20643 
20644 		addClass : function(elm, cls) {
20645 			return DOMUtils.DOM.addClass(elm, cls);
20646 		},
20647 
20648 		removeClass : function(elm, cls) {
20649 			return DOMUtils.DOM.removeClass(elm, cls);
20650 		},
20651 
20652 		hasClass : function(elm, cls) {
20653 			return DOMUtils.DOM.hasClass(elm, cls);
20654 		},
20655 
20656 		toggleClass: function(elm, cls, state) {
20657 			return DOMUtils.DOM.toggleClass(elm, cls, state);
20658 		},
20659 
20660 		css: function(elm, name, value) {
20661 			return DOMUtils.DOM.setStyle(elm, name, value);
20662 		},
20663 
20664 		on: function(target, name, callback, scope) {
20665 			return DOMUtils.DOM.bind(target, name, callback, scope);
20666 		},
20667 
20668 		off: function(target, name, callback) {
20669 			return DOMUtils.DOM.unbind(target, name, callback);
20670 		},
20671 
20672 		fire: function(target, name, args) {
20673 			return DOMUtils.DOM.fire(target, name, args);
20674 		},
20675 
20676 		innerHtml: function(elm, html) {
20677 			// Workaround for <div> in <p> bug on IE 8 #6178
20678 			DOMUtils.DOM.setHTML(elm, html);
20679 		}
20680 	};
20681 });
20682 
20683 // Included from: js/tinymce/classes/ui/Control.js
20684 
20685 /**
20686  * Control.js
20687  *
20688  * Copyright, Moxiecode Systems AB
20689  * Released under LGPL License.
20690  *
20691  * License: http://www.tinymce.com/license
20692  * Contributing: http://www.tinymce.com/contributing
20693  */
20694 
20695 /*eslint consistent-this:0 */
20696 
20697 /**
20698  * This is the base class for all controls and containers. All UI control instances inherit
20699  * from this one as it has the base logic needed by all of them.
20700  *
20701  * @class tinymce.ui.Control
20702  */
20703 define("tinymce/ui/Control", [
20704 	"tinymce/util/Class",
20705 	"tinymce/util/Tools",
20706 	"tinymce/util/EventDispatcher",
20707 	"tinymce/ui/Collection",
20708 	"tinymce/ui/DomUtils"
20709 ], function(Class, Tools, EventDispatcher, Collection, DomUtils) {
20710 	"use strict";
20711 
20712 	var elementIdCache = {};
20713 	var hasMouseWheelEventSupport = "onmousewheel" in document;
20714 	var hasWheelEventSupport = false;
20715 	var classPrefix = "mce-";
20716 
20717 	function getEventDispatcher(obj) {
20718 		if (!obj._eventDispatcher) {
20719 			obj._eventDispatcher = new EventDispatcher({
20720 				scope: obj,
20721 				toggleEvent: function(name, state) {
20722 					if (state && EventDispatcher.isNative(name)) {
20723 						if (!obj._nativeEvents) {
20724 							obj._nativeEvents = {};
20725 						}
20726 
20727 						obj._nativeEvents[name] = true;
20728 
20729 						if (obj._rendered) {
20730 							obj.bindPendingEvents();
20731 						}
20732 					}
20733 				}
20734 			});
20735 		}
20736 
20737 		return obj._eventDispatcher;
20738 	}
20739 
20740 	var Control = Class.extend({
20741 		Statics: {
20742 			elementIdCache: elementIdCache,
20743 			classPrefix: classPrefix
20744 		},
20745 
20746 		isRtl: function() {
20747 			return Control.rtl;
20748 		},
20749 
20750 		/**
20751 		 * Class/id prefix to use for all controls.
20752 		 *
20753 		 * @final
20754 		 * @field {String} classPrefix
20755 		 */
20756 		classPrefix: classPrefix,
20757 
20758 		/**
20759 		 * Constructs a new control instance with the specified settings.
20760 		 *
20761 		 * @constructor
20762 		 * @param {Object} settings Name/value object with settings.
20763 		 * @setting {String} style Style CSS properties to add.
20764 		 * @setting {String} border Border box values example: 1 1 1 1
20765 		 * @setting {String} padding Padding box values example: 1 1 1 1
20766 		 * @setting {String} margin Margin box values example: 1 1 1 1
20767 		 * @setting {Number} minWidth Minimal width for the control.
20768 		 * @setting {Number} minHeight Minimal height for the control.
20769 		 * @setting {String} classes Space separated list of classes to add.
20770 		 * @setting {String} role WAI-ARIA role to use for control.
20771 		 * @setting {Boolean} hidden Is the control hidden by default.
20772 		 * @setting {Boolean} disabled Is the control disabled by default.
20773 		 * @setting {String} name Name of the control instance.
20774 		 */
20775 		init: function(settings) {
20776 			var self = this, classes, i;
20777 
20778 			self.settings = settings = Tools.extend({}, self.Defaults, settings);
20779 
20780 			// Initial states
20781 			self._id = settings.id || DomUtils.id();
20782 			self._text = self._name = '';
20783 			self._width = self._height = 0;
20784 			self._aria = {role: settings.role};
20785 
20786 			// Setup classes
20787 			classes = settings.classes;
20788 			if (classes) {
20789 				classes = classes.split(' ');
20790 				classes.map = {};
20791 				i = classes.length;
20792 				while (i--) {
20793 					classes.map[classes[i]] = true;
20794 				}
20795 			}
20796 
20797 			self._classes = classes || [];
20798 			self.visible(true);
20799 
20800 			// Set some properties
20801 			Tools.each('title text width height name classes visible disabled active value'.split(' '), function(name) {
20802 				var value = settings[name], undef;
20803 
20804 				if (value !== undef) {
20805 					self[name](value);
20806 				} else if (self['_' + name] === undef) {
20807 					self['_' + name] = false;
20808 				}
20809 			});
20810 
20811 			self.on('click', function() {
20812 				if (self.disabled()) {
20813 					return false;
20814 				}
20815 			});
20816 
20817 			// TODO: Is this needed duplicate code see above?
20818 			if (settings.classes) {
20819 				Tools.each(settings.classes.split(' '), function(cls) {
20820 					self.addClass(cls);
20821 				});
20822 			}
20823 
20824 			/**
20825 			 * Name/value object with settings for the current control.
20826 			 *
20827 			 * @field {Object} settings
20828 			 */
20829 			self.settings = settings;
20830 
20831 			self._borderBox = self.parseBox(settings.border);
20832 			self._paddingBox = self.parseBox(settings.padding);
20833 			self._marginBox = self.parseBox(settings.margin);
20834 
20835 			if (settings.hidden) {
20836 				self.hide();
20837 			}
20838 		},
20839 
20840 		// Will generate getter/setter methods for these properties
20841 		Properties: 'parent,title,text,width,height,disabled,active,name,value',
20842 
20843 		// Will generate empty dummy functions for these
20844 		Methods: 'renderHtml',
20845 
20846 		/**
20847 		 * Returns the root element to render controls into.
20848 		 *
20849 		 * @method getContainerElm
20850 		 * @return {Element} HTML DOM element to render into.
20851 		 */
20852 		getContainerElm: function() {
20853 			return document.body;
20854 		},
20855 
20856 		/**
20857 		 * Returns a control instance for the current DOM element.
20858 		 *
20859 		 * @method getParentCtrl
20860 		 * @param {Element} elm HTML dom element to get parent control from.
20861 		 * @return {tinymce.ui.Control} Control instance or undefined.
20862 		 */
20863 		getParentCtrl: function(elm) {
20864 			var ctrl, lookup = this.getRoot().controlIdLookup;
20865 
20866 			while (elm && lookup) {
20867 				ctrl = lookup[elm.id];
20868 				if (ctrl) {
20869 					break;
20870 				}
20871 
20872 				elm = elm.parentNode;
20873 			}
20874 
20875 			return ctrl;
20876 		},
20877 
20878 		/**
20879 		 * Parses the specified box value. A box value contains 1-4 properties in clockwise order.
20880 		 *
20881 		 * @method parseBox
20882 		 * @param {String/Number} value Box value "0 1 2 3" or "0" etc.
20883 		 * @return {Object} Object with top/right/bottom/left properties.
20884 		 * @private
20885 		 */
20886 		parseBox: function(value) {
20887 			var len, radix = 10;
20888 
20889 			if (!value) {
20890 				return;
20891 			}
20892 
20893 			if (typeof(value) === "number") {
20894 				value = value || 0;
20895 
20896 				return {
20897 					top: value,
20898 					left: value,
20899 					bottom: value,
20900 					right: value
20901 				};
20902 			}
20903 
20904 			value = value.split(' ');
20905 			len = value.length;
20906 
20907 			if (len === 1) {
20908 				value[1] = value[2] = value[3] = value[0];
20909 			} else if (len === 2) {
20910 				value[2] = value[0];
20911 				value[3] = value[1];
20912 			} else if (len === 3) {
20913 				value[3] = value[1];
20914 			}
20915 
20916 			return {
20917 				top: parseInt(value[0], radix) || 0,
20918 				right: parseInt(value[1], radix) || 0,
20919 				bottom: parseInt(value[2], radix) || 0,
20920 				left: parseInt(value[3], radix) || 0
20921 			};
20922 		},
20923 
20924 		borderBox: function() {
20925 			return this._borderBox;
20926 		},
20927 
20928 		paddingBox: function() {
20929 			return this._paddingBox;
20930 		},
20931 
20932 		marginBox: function() {
20933 			return this._marginBox;
20934 		},
20935 
20936 		measureBox: function(elm, prefix) {
20937 			function getStyle(name) {
20938 				var defaultView = document.defaultView;
20939 
20940 				if (defaultView) {
20941 					// Remove camelcase
20942 					name = name.replace(/[A-Z]/g, function(a) {
20943 						return '-' + a;
20944 					});
20945 
20946 					return defaultView.getComputedStyle(elm, null).getPropertyValue(name);
20947 				}
20948 
20949 				return elm.currentStyle[name];
20950 			}
20951 
20952 			function getSide(name) {
20953 				var val = parseFloat(getStyle(name), 10);
20954 
20955 				return isNaN(val) ? 0 : val;
20956 			}
20957 
20958 			return {
20959 				top: getSide(prefix + "TopWidth"),
20960 				right: getSide(prefix + "RightWidth"),
20961 				bottom: getSide(prefix + "BottomWidth"),
20962 				left: getSide(prefix + "LeftWidth")
20963 			};
20964 		},
20965 
20966 		/**
20967 		 * Initializes the current controls layout rect.
20968 		 * This will be executed by the layout managers to determine the
20969 		 * default minWidth/minHeight etc.
20970 		 *
20971 		 * @method initLayoutRect
20972 		 * @return {Object} Layout rect instance.
20973 		 */
20974 		initLayoutRect: function() {
20975 			var self = this, settings = self.settings, borderBox, layoutRect;
20976 			var elm = self.getEl(), width, height, minWidth, minHeight, autoResize;
20977 			var startMinWidth, startMinHeight, initialSize;
20978 
20979 			// Measure the current element
20980 			borderBox = self._borderBox = self._borderBox || self.measureBox(elm, 'border');
20981 			self._paddingBox = self._paddingBox || self.measureBox(elm, 'padding');
20982 			self._marginBox = self._marginBox || self.measureBox(elm, 'margin');
20983 			initialSize = DomUtils.getSize(elm);
20984 
20985 			// Setup minWidth/minHeight and width/height
20986 			startMinWidth = settings.minWidth;
20987 			startMinHeight = settings.minHeight;
20988 			minWidth = startMinWidth || initialSize.width;
20989 			minHeight = startMinHeight || initialSize.height;
20990 			width = settings.width;
20991 			height = settings.height;
20992 			autoResize = settings.autoResize;
20993 			autoResize = typeof(autoResize) != "undefined" ? autoResize : !width && !height;
20994 
20995 			width = width || minWidth;
20996 			height = height || minHeight;
20997 
20998 			var deltaW = borderBox.left + borderBox.right;
20999 			var deltaH = borderBox.top + borderBox.bottom;
21000 
21001 			var maxW = settings.maxWidth || 0xFFFF;
21002 			var maxH = settings.maxHeight || 0xFFFF;
21003 
21004 			// Setup initial layout rect
21005 			self._layoutRect = layoutRect = {
21006 				x: settings.x || 0,
21007 				y: settings.y || 0,
21008 				w: width,
21009 				h: height,
21010 				deltaW: deltaW,
21011 				deltaH: deltaH,
21012 				contentW: width - deltaW,
21013 				contentH: height - deltaH,
21014 				innerW: width - deltaW,
21015 				innerH: height - deltaH,
21016 				startMinWidth: startMinWidth || 0,
21017 				startMinHeight: startMinHeight || 0,
21018 				minW: Math.min(minWidth, maxW),
21019 				minH: Math.min(minHeight, maxH),
21020 				maxW: maxW,
21021 				maxH: maxH,
21022 				autoResize: autoResize,
21023 				scrollW: 0
21024 			};
21025 
21026 			self._lastLayoutRect = {};
21027 
21028 			return layoutRect;
21029 		},
21030 
21031 		/**
21032 		 * Getter/setter for the current layout rect.
21033 		 *
21034 		 * @method layoutRect
21035 		 * @param {Object} [newRect] Optional new layout rect.
21036 		 * @return {tinymce.ui.Control/Object} Current control or rect object.
21037 		 */
21038 		layoutRect: function(newRect) {
21039 			var self = this, curRect = self._layoutRect, lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls;
21040 
21041 			// Initialize default layout rect
21042 			if (!curRect) {
21043 				curRect = self.initLayoutRect();
21044 			}
21045 
21046 			// Set new rect values
21047 			if (newRect) {
21048 				// Calc deltas between inner and outer sizes
21049 				deltaWidth = curRect.deltaW;
21050 				deltaHeight = curRect.deltaH;
21051 
21052 				// Set x position
21053 				if (newRect.x !== undef) {
21054 					curRect.x = newRect.x;
21055 				}
21056 
21057 				// Set y position
21058 				if (newRect.y !== undef) {
21059 					curRect.y = newRect.y;
21060 				}
21061 
21062 				// Set minW
21063 				if (newRect.minW !== undef) {
21064 					curRect.minW = newRect.minW;
21065 				}
21066 
21067 				// Set minH
21068 				if (newRect.minH !== undef) {
21069 					curRect.minH = newRect.minH;
21070 				}
21071 
21072 				// Set new width and calculate inner width
21073 				size = newRect.w;
21074 				if (size !== undef) {
21075 					size = size < curRect.minW ? curRect.minW : size;
21076 					size = size > curRect.maxW ? curRect.maxW : size;
21077 					curRect.w = size;
21078 					curRect.innerW = size - deltaWidth;
21079 				}
21080 
21081 				// Set new height and calculate inner height
21082 				size = newRect.h;
21083 				if (size !== undef) {
21084 					size = size < curRect.minH ? curRect.minH : size;
21085 					size = size > curRect.maxH ? curRect.maxH : size;
21086 					curRect.h = size;
21087 					curRect.innerH = size - deltaHeight;
21088 				}
21089 
21090 				// Set new inner width and calculate width
21091 				size = newRect.innerW;
21092 				if (size !== undef) {
21093 					size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size;
21094 					size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size;
21095 					curRect.innerW = size;
21096 					curRect.w = size + deltaWidth;
21097 				}
21098 
21099 				// Set new height and calculate inner height
21100 				size = newRect.innerH;
21101 				if (size !== undef) {
21102 					size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size;
21103 					size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size;
21104 					curRect.innerH = size;
21105 					curRect.h = size + deltaHeight;
21106 				}
21107 
21108 				// Set new contentW
21109 				if (newRect.contentW !== undef) {
21110 					curRect.contentW = newRect.contentW;
21111 				}
21112 
21113 				// Set new contentH
21114 				if (newRect.contentH !== undef) {
21115 					curRect.contentH = newRect.contentH;
21116 				}
21117 
21118 				// Compare last layout rect with the current one to see if we need to repaint or not
21119 				lastLayoutRect = self._lastLayoutRect;
21120 				if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y ||
21121 					lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) {
21122 					repaintControls = Control.repaintControls;
21123 
21124 					if (repaintControls) {
21125 						if (repaintControls.map && !repaintControls.map[self._id]) {
21126 							repaintControls.push(self);
21127 							repaintControls.map[self._id] = true;
21128 						}
21129 					}
21130 
21131 					lastLayoutRect.x = curRect.x;
21132 					lastLayoutRect.y = curRect.y;
21133 					lastLayoutRect.w = curRect.w;
21134 					lastLayoutRect.h = curRect.h;
21135 				}
21136 
21137 				return self;
21138 			}
21139 
21140 			return curRect;
21141 		},
21142 
21143 		/**
21144 		 * Repaints the control after a layout operation.
21145 		 *
21146 		 * @method repaint
21147 		 */
21148 		repaint: function() {
21149 			var self = this, style, bodyStyle, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect, round;
21150 
21151 			// Use Math.round on all values on IE < 9
21152 			round = !document.createRange ? Math.round : function(value) {
21153 				return value;
21154 			};
21155 
21156 			style = self.getEl().style;
21157 			rect = self._layoutRect;
21158 			lastRepaintRect = self._lastRepaintRect || {};
21159 
21160 			borderBox = self._borderBox;
21161 			borderW = borderBox.left + borderBox.right;
21162 			borderH = borderBox.top + borderBox.bottom;
21163 
21164 			if (rect.x !== lastRepaintRect.x) {
21165 				style.left = round(rect.x) + 'px';
21166 				lastRepaintRect.x = rect.x;
21167 			}
21168 
21169 			if (rect.y !== lastRepaintRect.y) {
21170 				style.top = round(rect.y) + 'px';
21171 				lastRepaintRect.y = rect.y;
21172 			}
21173 
21174 			if (rect.w !== lastRepaintRect.w) {
21175 				style.width = round(rect.w - borderW) + 'px';
21176 				lastRepaintRect.w = rect.w;
21177 			}
21178 
21179 			if (rect.h !== lastRepaintRect.h) {
21180 				style.height = round(rect.h - borderH) + 'px';
21181 				lastRepaintRect.h = rect.h;
21182 			}
21183 
21184 			// Update body if needed
21185 			if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) {
21186 				bodyStyle = self.getEl('body').style;
21187 				bodyStyle.width = round(rect.innerW) + 'px';
21188 				lastRepaintRect.innerW = rect.innerW;
21189 			}
21190 
21191 			if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) {
21192 				bodyStyle = bodyStyle || self.getEl('body').style;
21193 				bodyStyle.height = round(rect.innerH) + 'px';
21194 				lastRepaintRect.innerH = rect.innerH;
21195 			}
21196 
21197 			self._lastRepaintRect = lastRepaintRect;
21198 			self.fire('repaint', {}, false);
21199 		},
21200 
21201 		/**
21202 		 * Binds a callback to the specified event. This event can both be
21203 		 * native browser events like "click" or custom ones like PostRender.
21204 		 *
21205 		 * The callback function will be passed a DOM event like object that enables yout do stop propagation.
21206 		 *
21207 		 * @method on
21208 		 * @param {String} name Name of the event to bind. For example "click".
21209 		 * @param {String/function} callback Callback function to execute ones the event occurs.
21210 		 * @return {tinymce.ui.Control} Current control object.
21211 		 */
21212 		on: function(name, callback) {
21213 			var self = this;
21214 
21215 			function resolveCallbackName(name) {
21216 				var callback, scope;
21217 
21218 				if (typeof(name) != 'string') {
21219 					return name;
21220 				}
21221 
21222 				return function(e) {
21223 					if (!callback) {
21224 						self.parentsAndSelf().each(function(ctrl) {
21225 							var callbacks = ctrl.settings.callbacks;
21226 
21227 							if (callbacks && (callback = callbacks[name])) {
21228 								scope = ctrl;
21229 								return false;
21230 							}
21231 						});
21232 					}
21233 
21234 					return callback.call(scope, e);
21235 				};
21236 			}
21237 
21238 			getEventDispatcher(self).on(name, resolveCallbackName(callback));
21239 
21240 			return self;
21241 		},
21242 
21243 		/**
21244 		 * Unbinds the specified event and optionally a specific callback. If you omit the name
21245 		 * parameter all event handlers will be removed. If you omit the callback all event handles
21246 		 * by the specified name will be removed.
21247 		 *
21248 		 * @method off
21249 		 * @param {String} [name] Name for the event to unbind.
21250 		 * @param {function} [callback] Callback function to unbind.
21251 		 * @return {mxex.ui.Control} Current control object.
21252 		 */
21253 		off: function(name, callback) {
21254 			getEventDispatcher(this).off(name, callback);
21255 			return this;
21256 		},
21257 
21258 		/**
21259 		 * Fires the specified event by name and arguments on the control. This will execute all
21260 		 * bound event handlers.
21261 		 *
21262 		 * @method fire
21263 		 * @param {String} name Name of the event to fire.
21264 		 * @param {Object} [args] Arguments to pass to the event.
21265 		 * @param {Boolean} [bubble] Value to control bubbeling. Defaults to true.
21266 		 * @return {Object} Current arguments object.
21267 		 */
21268 		fire: function(name, args, bubble) {
21269 			var self = this;
21270 
21271 			args = args || {};
21272 
21273 			if (!args.control) {
21274 				args.control = self;
21275 			}
21276 
21277 			args = getEventDispatcher(self).fire(name, args);
21278 
21279 			// Bubble event up to parents
21280 			if (bubble !== false && self.parent) {
21281 				var parent = self.parent();
21282 				while (parent && !args.isPropagationStopped()) {
21283 					parent.fire(name, args, false);
21284 					parent = parent.parent();
21285 				}
21286 			}
21287 
21288 			return args;
21289 		},
21290 
21291 		/**
21292 		 * Returns true/false if the specified event has any listeners.
21293 		 *
21294 		 * @method hasEventListeners
21295 		 * @param {String} name Name of the event to check for.
21296 		 * @return {Boolean} True/false state if the event has listeners.
21297 		 */
21298 		hasEventListeners: function(name) {
21299 			return getEventDispatcher(this).has(name);
21300 		},
21301 
21302 		/**
21303 		 * Returns a control collection with all parent controls.
21304 		 *
21305 		 * @method parents
21306 		 * @param {String} selector Optional selector expression to find parents.
21307 		 * @return {tinymce.ui.Collection} Collection with all parent controls.
21308 		 */
21309 		parents: function(selector) {
21310 			var self = this, ctrl, parents = new Collection();
21311 
21312 			// Add each parent to collection
21313 			for (ctrl = self.parent(); ctrl; ctrl = ctrl.parent()) {
21314 				parents.add(ctrl);
21315 			}
21316 
21317 			// Filter away everything that doesn't match the selector
21318 			if (selector) {
21319 				parents = parents.filter(selector);
21320 			}
21321 
21322 			return parents;
21323 		},
21324 
21325 		/**
21326 		 * Returns the current control and it's parents.
21327 		 *
21328 		 * @method parentsAndSelf
21329 		 * @param {String} selector Optional selector expression to find parents.
21330 		 * @return {tinymce.ui.Collection} Collection with all parent controls.
21331 		 */
21332 		parentsAndSelf: function(selector) {
21333 			return new Collection(this).add(this.parents(selector));
21334 		},
21335 
21336 		/**
21337 		 * Returns the control next to the current control.
21338 		 *
21339 		 * @method next
21340 		 * @return {tinymce.ui.Control} Next control instance.
21341 		 */
21342 		next: function() {
21343 			var parentControls = this.parent().items();
21344 
21345 			return parentControls[parentControls.indexOf(this) + 1];
21346 		},
21347 
21348 		/**
21349 		 * Returns the control previous to the current control.
21350 		 *
21351 		 * @method prev
21352 		 * @return {tinymce.ui.Control} Previous control instance.
21353 		 */
21354 		prev: function() {
21355 			var parentControls = this.parent().items();
21356 
21357 			return parentControls[parentControls.indexOf(this) - 1];
21358 		},
21359 
21360 		/**
21361 		 * Find the common ancestor for two control instances.
21362 		 *
21363 		 * @method findCommonAncestor
21364 		 * @param {tinymce.ui.Control} ctrl1 First control.
21365 		 * @param {tinymce.ui.Control} ctrl2 Second control.
21366 		 * @return {tinymce.ui.Control} Ancestor control instance.
21367 		 */
21368 		findCommonAncestor: function(ctrl1, ctrl2) {
21369 			var parentCtrl;
21370 
21371 			while (ctrl1) {
21372 				parentCtrl = ctrl2;
21373 
21374 				while (parentCtrl && ctrl1 != parentCtrl) {
21375 					parentCtrl = parentCtrl.parent();
21376 				}
21377 
21378 				if (ctrl1 == parentCtrl) {
21379 					break;
21380 				}
21381 
21382 				ctrl1 = ctrl1.parent();
21383 			}
21384 
21385 			return ctrl1;
21386 		},
21387 
21388 		/**
21389 		 * Returns true/false if the specific control has the specific class.
21390 		 *
21391 		 * @method hasClass
21392 		 * @param {String} cls Class to check for.
21393 		 * @param {String} [group] Sub element group name.
21394 		 * @return {Boolean} True/false if the control has the specified class.
21395 		 */
21396 		hasClass: function(cls, group) {
21397 			var classes = this._classes[group || 'control'];
21398 
21399 			cls = this.classPrefix + cls;
21400 
21401 			return classes && !!classes.map[cls];
21402 		},
21403 
21404 		/**
21405 		 * Adds the specified class to the control
21406 		 *
21407 		 * @method addClass
21408 		 * @param {String} cls Class to check for.
21409 		 * @param {String} [group] Sub element group name.
21410 		 * @return {tinymce.ui.Control} Current control object.
21411 		 */
21412 		addClass: function(cls, group) {
21413 			var self = this, classes, elm;
21414 
21415 			cls = this.classPrefix + cls;
21416 			classes = self._classes[group || 'control'];
21417 
21418 			if (!classes) {
21419 				classes = [];
21420 				classes.map = {};
21421 				self._classes[group || 'control'] = classes;
21422 			}
21423 
21424 			if (!classes.map[cls]) {
21425 				classes.map[cls] = cls;
21426 				classes.push(cls);
21427 
21428 				if (self._rendered) {
21429 					elm = self.getEl(group);
21430 
21431 					if (elm) {
21432 						elm.className = classes.join(' ');
21433 					}
21434 				}
21435 			}
21436 
21437 			return self;
21438 		},
21439 
21440 		/**
21441 		 * Removes the specified class from the control.
21442 		 *
21443 		 * @method removeClass
21444 		 * @param {String} cls Class to remove.
21445 		 * @param {String} [group] Sub element group name.
21446 		 * @return {tinymce.ui.Control} Current control object.
21447 		 */
21448 		removeClass: function(cls, group) {
21449 			var self = this, classes, i, elm;
21450 
21451 			cls = this.classPrefix + cls;
21452 			classes = self._classes[group || 'control'];
21453 			if (classes && classes.map[cls]) {
21454 				delete classes.map[cls];
21455 
21456 				i = classes.length;
21457 				while (i--) {
21458 					if (classes[i] === cls) {
21459 						classes.splice(i, 1);
21460 					}
21461 				}
21462 			}
21463 
21464 			if (self._rendered) {
21465 				elm = self.getEl(group);
21466 
21467 				if (elm) {
21468 					elm.className = classes.join(' ');
21469 				}
21470 			}
21471 
21472 			return self;
21473 		},
21474 
21475 		/**
21476 		 * Toggles the specified class on the control.
21477 		 *
21478 		 * @method toggleClass
21479 		 * @param {String} cls Class to remove.
21480 		 * @param {Boolean} state True/false state to add/remove class.
21481 		 * @param {String} [group] Sub element group name.
21482 		 * @return {tinymce.ui.Control} Current control object.
21483 		 */
21484 		toggleClass: function(cls, state, group) {
21485 			var self = this;
21486 
21487 			if (state) {
21488 				self.addClass(cls, group);
21489 			} else {
21490 				self.removeClass(cls, group);
21491 			}
21492 
21493 			return self;
21494 		},
21495 
21496 		/**
21497 		 * Returns the class string for the specified group name.
21498 		 *
21499 		 * @method classes
21500 		 * @param {String} [group] Group to get clases by.
21501 		 * @return {String} Classes for the specified group.
21502 		 */
21503 		classes: function(group) {
21504 			var classes = this._classes[group || 'control'];
21505 
21506 			return classes ? classes.join(' ') : '';
21507 		},
21508 
21509 		/**
21510 		 * Sets the inner HTML of the control element.
21511 		 *
21512 		 * @method innerHtml
21513 		 * @param {String} html Html string to set as inner html.
21514 		 * @return {tinymce.ui.Control} Current control object.
21515 		 */
21516 		innerHtml: function(html) {
21517 			DomUtils.innerHtml(this.getEl(), html);
21518 			return this;
21519 		},
21520 
21521 		/**
21522 		 * Returns the control DOM element or sub element.
21523 		 *
21524 		 * @method getEl
21525 		 * @param {String} [suffix] Suffix to get element by.
21526 		 * @param {Boolean} [dropCache] True if the cache for the element should be dropped.
21527 		 * @return {Element} HTML DOM element for the current control or it's children.
21528 		 */
21529 		getEl: function(suffix, dropCache) {
21530 			var elm, id = suffix ? this._id + '-' + suffix : this._id;
21531 
21532 			elm = elementIdCache[id] = (dropCache === true ? null : elementIdCache[id]) || DomUtils.get(id);
21533 
21534 			return elm;
21535 		},
21536 
21537 		/**
21538 		 * Sets/gets the visible for the control.
21539 		 *
21540 		 * @method visible
21541 		 * @param {Boolean} state Value to set to control.
21542 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
21543 		 */
21544 		visible: function(state) {
21545 			var self = this, parentCtrl;
21546 
21547 			if (typeof(state) !== "undefined") {
21548 				if (self._visible !== state) {
21549 					if (self._rendered) {
21550 						self.getEl().style.display = state ? '' : 'none';
21551 					}
21552 
21553 					self._visible = state;
21554 
21555 					// Parent container needs to reflow
21556 					parentCtrl = self.parent();
21557 					if (parentCtrl) {
21558 						parentCtrl._lastRect = null;
21559 					}
21560 
21561 					self.fire(state ? 'show' : 'hide');
21562 				}
21563 
21564 				return self;
21565 			}
21566 
21567 			return self._visible;
21568 		},
21569 
21570 		/**
21571 		 * Sets the visible state to true.
21572 		 *
21573 		 * @method show
21574 		 * @return {tinymce.ui.Control} Current control instance.
21575 		 */
21576 		show: function() {
21577 			return this.visible(true);
21578 		},
21579 
21580 		/**
21581 		 * Sets the visible state to false.
21582 		 *
21583 		 * @method hide
21584 		 * @return {tinymce.ui.Control} Current control instance.
21585 		 */
21586 		hide: function() {
21587 			return this.visible(false);
21588 		},
21589 
21590 		/**
21591 		 * Focuses the current control.
21592 		 *
21593 		 * @method focus
21594 		 * @return {tinymce.ui.Control} Current control instance.
21595 		 */
21596 		focus: function() {
21597 			try {
21598 				this.getEl().focus();
21599 			} catch (ex) {
21600 				// Ignore IE error
21601 			}
21602 
21603 			return this;
21604 		},
21605 
21606 		/**
21607 		 * Blurs the current control.
21608 		 *
21609 		 * @method blur
21610 		 * @return {tinymce.ui.Control} Current control instance.
21611 		 */
21612 		blur: function() {
21613 			this.getEl().blur();
21614 
21615 			return this;
21616 		},
21617 
21618 		/**
21619 		 * Sets the specified aria property.
21620 		 *
21621 		 * @method aria
21622 		 * @param {String} name Name of the aria property to set.
21623 		 * @param {String} value Value of the aria property.
21624 		 * @return {tinymce.ui.Control} Current control instance.
21625 		 */
21626 		aria: function(name, value) {
21627 			var self = this, elm = self.getEl(self.ariaTarget);
21628 
21629 			if (typeof(value) === "undefined") {
21630 				return self._aria[name];
21631 			} else {
21632 				self._aria[name] = value;
21633 			}
21634 
21635 			if (self._rendered) {
21636 				elm.setAttribute(name == 'role' ? name : 'aria-' + name, value);
21637 			}
21638 
21639 			return self;
21640 		},
21641 
21642 		/**
21643 		 * Encodes the specified string with HTML entities. It will also
21644 		 * translate the string to different languages.
21645 		 *
21646 		 * @method encode
21647 		 * @param {String/Object/Array} text Text to entity encode.
21648 		 * @param {Boolean} [translate=true] False if the contents shouldn't be translated.
21649 		 * @return {String} Encoded and possible traslated string. 
21650 		 */
21651 		encode: function(text, translate) {
21652 			if (translate !== false) {
21653 				text = this.translate(text);
21654 			}
21655 
21656 			return (text || '').replace(/[&<>"]/g, function(match) {
21657 				return '&#' + match.charCodeAt(0) + ';';
21658 			});
21659 		},
21660 
21661 		/**
21662 		 * Returns the translated string.
21663 		 *
21664 		 * @method translate
21665 		 * @param {String} text Text to translate.
21666 		 * @return {String} Translated string or the same as the input.
21667 		 */
21668 		translate: function(text) {
21669 			return Control.translate ? Control.translate(text) : text;
21670 		},
21671 
21672 		/**
21673 		 * Adds items before the current control.
21674 		 *
21675 		 * @method before
21676 		 * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control.
21677 		 * @return {tinymce.ui.Control} Current control instance.
21678 		 */
21679 		before: function(items) {
21680 			var self = this, parent = self.parent();
21681 
21682 			if (parent) {
21683 				parent.insert(items, parent.items().indexOf(self), true);
21684 			}
21685 
21686 			return self;
21687 		},
21688 
21689 		/**
21690 		 * Adds items after the current control.
21691 		 *
21692 		 * @method after
21693 		 * @param {Array/tinymce.ui.Collection} items Array of items to append after this control.
21694 		 * @return {tinymce.ui.Control} Current control instance.
21695 		 */
21696 		after: function(items) {
21697 			var self = this, parent = self.parent();
21698 
21699 			if (parent) {
21700 				parent.insert(items, parent.items().indexOf(self));
21701 			}
21702 
21703 			return self;
21704 		},
21705 
21706 		/**
21707 		 * Removes the current control from DOM and from UI collections.
21708 		 *
21709 		 * @method remove
21710 		 * @return {tinymce.ui.Control} Current control instance.
21711 		 */
21712 		remove: function() {
21713 			var self = this, elm = self.getEl(), parent = self.parent(), newItems, i;
21714 
21715 			if (self.items) {
21716 				var controls = self.items().toArray();
21717 				i = controls.length;
21718 				while (i--) {
21719 					controls[i].remove();
21720 				}
21721 			}
21722 
21723 			if (parent && parent.items) {
21724 				newItems = [];
21725 
21726 				parent.items().each(function(item) {
21727 					if (item !== self) {
21728 						newItems.push(item);
21729 					}
21730 				});
21731 
21732 				parent.items().set(newItems);
21733 				parent._lastRect = null;
21734 			}
21735 
21736 			if (self._eventsRoot && self._eventsRoot == self) {
21737 				DomUtils.off(elm);
21738 			}
21739 
21740 			var lookup = self.getRoot().controlIdLookup;
21741 			if (lookup) {
21742 				delete lookup[self._id];
21743 			}
21744 
21745 			delete elementIdCache[self._id];
21746 
21747 			if (elm && elm.parentNode) {
21748 				var nodes = elm.getElementsByTagName('*');
21749 
21750 				i = nodes.length;
21751 				while (i--) {
21752 					delete elementIdCache[nodes[i].id];
21753 				}
21754 
21755 				elm.parentNode.removeChild(elm);
21756 			}
21757 
21758 			self._rendered = false;
21759 
21760 			return self;
21761 		},
21762 
21763 		/**
21764 		 * Renders the control before the specified element.
21765 		 *
21766 		 * @method renderBefore
21767 		 * @param {Element} elm Element to render before.
21768 		 * @return {tinymce.ui.Control} Current control instance.
21769 		 */
21770 		renderBefore: function(elm) {
21771 			var self = this;
21772 
21773 			elm.parentNode.insertBefore(DomUtils.createFragment(self.renderHtml()), elm);
21774 			self.postRender();
21775 
21776 			return self;
21777 		},
21778 
21779 		/**
21780 		 * Renders the control to the specified element.
21781 		 *
21782 		 * @method renderBefore
21783 		 * @param {Element} elm Element to render to.
21784 		 * @return {tinymce.ui.Control} Current control instance.
21785 		 */
21786 		renderTo: function(elm) {
21787 			var self = this;
21788 
21789 			elm = elm || self.getContainerElm();
21790 			elm.appendChild(DomUtils.createFragment(self.renderHtml()));
21791 			self.postRender();
21792 
21793 			return self;
21794 		},
21795 
21796 		/**
21797 		 * Post render method. Called after the control has been rendered to the target.
21798 		 *
21799 		 * @method postRender
21800 		 * @return {tinymce.ui.Control} Current control instance.
21801 		 */
21802 		postRender: function() {
21803 			var self = this, settings = self.settings, elm, box, parent, name, parentEventsRoot;
21804 
21805 			// Bind on<event> settings
21806 			for (name in settings) {
21807 				if (name.indexOf("on") === 0) {
21808 					self.on(name.substr(2), settings[name]);
21809 				}
21810 			}
21811 
21812 			if (self._eventsRoot) {
21813 				for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) {
21814 					parentEventsRoot = parent._eventsRoot;
21815 				}
21816 
21817 				if (parentEventsRoot) {
21818 					for (name in parentEventsRoot._nativeEvents) {
21819 						self._nativeEvents[name] = true;
21820 					}
21821 				}
21822 			}
21823 
21824 			self.bindPendingEvents();
21825 
21826 			if (settings.style) {
21827 				elm = self.getEl();
21828 				if (elm) {
21829 					elm.setAttribute('style', settings.style);
21830 					elm.style.cssText = settings.style;
21831 				}
21832 			}
21833 
21834 			if (!self._visible) {
21835 				DomUtils.css(self.getEl(), 'display', 'none');
21836 			}
21837 
21838 			if (self.settings.border) {
21839 				box = self.borderBox();
21840 				DomUtils.css(self.getEl(), {
21841 					'border-top-width': box.top,
21842 					'border-right-width': box.right,
21843 					'border-bottom-width': box.bottom,
21844 					'border-left-width': box.left
21845 				});
21846 			}
21847 
21848 			// Add instance to lookup
21849 			var root = self.getRoot();
21850 			if (!root.controlIdLookup) {
21851 				root.controlIdLookup = {};
21852 			}
21853 
21854 			root.controlIdLookup[self._id] = self;
21855 
21856 			for (var key in self._aria) {
21857 				self.aria(key, self._aria[key]);
21858 			}
21859 
21860 			self.fire('postrender', {}, false);
21861 		},
21862 
21863 		/**
21864 		 * Scrolls the current control into view.
21865 		 *
21866 		 * @method scrollIntoView
21867 		 * @param {String} align Alignment in view top|center|bottom.
21868 		 * @return {tinymce.ui.Control} Current control instance.
21869 		 */
21870 		scrollIntoView: function(align) {
21871 			function getOffset(elm, rootElm) {
21872 				var x, y, parent = elm;
21873 
21874 				x = y = 0;
21875 				while (parent && parent != rootElm && parent.nodeType) {
21876 					x += parent.offsetLeft || 0;
21877 					y += parent.offsetTop || 0;
21878 					parent = parent.offsetParent;
21879 				}
21880 
21881 				return {x: x, y: y};
21882 			}
21883 
21884 			var elm = this.getEl(), parentElm = elm.parentNode;
21885 			var x, y, width, height, parentWidth, parentHeight;
21886 			var pos = getOffset(elm, parentElm);
21887 
21888 			x = pos.x;
21889 			y = pos.y;
21890 			width = elm.offsetWidth;
21891 			height = elm.offsetHeight;
21892 			parentWidth = parentElm.clientWidth;
21893 			parentHeight = parentElm.clientHeight;
21894 
21895 			if (align == "end") {
21896 				x -= parentWidth - width;
21897 				y -= parentHeight - height;
21898 			} else if (align == "center") {
21899 				x -= (parentWidth / 2) - (width / 2);
21900 				y -= (parentHeight / 2) - (height / 2);
21901 			}
21902 
21903 			parentElm.scrollLeft = x;
21904 			parentElm.scrollTop = y;
21905 
21906 			return this;
21907 		},
21908 
21909 		/**
21910 		 * Binds pending DOM events.
21911 		 *
21912 		 * @private
21913 		 */
21914 		bindPendingEvents: function() {
21915 			var self = this, i, l, parents, eventRootCtrl, nativeEvents, name;
21916 
21917 			function delegate(e) {
21918 				var control = self.getParentCtrl(e.target);
21919 
21920 				if (control) {
21921 					control.fire(e.type, e);
21922 				}
21923 			}
21924 
21925 			function mouseLeaveHandler() {
21926 				var ctrl = eventRootCtrl._lastHoverCtrl;
21927 
21928 				if (ctrl) {
21929 					ctrl.fire("mouseleave", {target: ctrl.getEl()});
21930 
21931 					ctrl.parents().each(function(ctrl) {
21932 						ctrl.fire("mouseleave", {target: ctrl.getEl()});
21933 					});
21934 
21935 					eventRootCtrl._lastHoverCtrl = null;
21936 				}
21937 			}
21938 
21939 			function mouseEnterHandler(e) {
21940 				var ctrl = self.getParentCtrl(e.target), lastCtrl = eventRootCtrl._lastHoverCtrl, idx = 0, i, parents, lastParents;
21941 
21942 				// Over on a new control
21943 				if (ctrl !== lastCtrl) {
21944 					eventRootCtrl._lastHoverCtrl = ctrl;
21945 
21946 					parents = ctrl.parents().toArray().reverse();
21947 					parents.push(ctrl);
21948 
21949 					if (lastCtrl) {
21950 						lastParents = lastCtrl.parents().toArray().reverse();
21951 						lastParents.push(lastCtrl);
21952 
21953 						for (idx = 0; idx < lastParents.length; idx++) {
21954 							if (parents[idx] !== lastParents[idx]) {
21955 								break;
21956 							}
21957 						}
21958 
21959 						for (i = lastParents.length - 1; i >= idx; i--) {
21960 							lastCtrl = lastParents[i];
21961 							lastCtrl.fire("mouseleave", {
21962 								target : lastCtrl.getEl()
21963 							});
21964 						}
21965 					}
21966 
21967 					for (i = idx; i < parents.length; i++) {
21968 						ctrl = parents[i];
21969 						ctrl.fire("mouseenter", {
21970 							target : ctrl.getEl()
21971 						});
21972 					}
21973 				}
21974 			}
21975 
21976 			function fixWheelEvent(e) {
21977 				e.preventDefault();
21978 
21979 				if (e.type == "mousewheel") {
21980 					e.deltaY = -1 / 40 * e.wheelDelta;
21981 
21982 					if (e.wheelDeltaX) {
21983 						e.deltaX = -1 / 40 * e.wheelDeltaX;
21984 					}
21985 				} else {
21986 					e.deltaX = 0;
21987 					e.deltaY = e.detail;
21988 				}
21989 
21990 				e = self.fire("wheel", e);
21991 			}
21992 
21993 			self._rendered = true;
21994 
21995 			nativeEvents = self._nativeEvents;
21996 			if (nativeEvents) {
21997 				// Find event root element if it exists
21998 				parents = self.parents().toArray();
21999 				parents.unshift(self);
22000 				for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) {
22001 					eventRootCtrl = parents[i]._eventsRoot;
22002 				}
22003 
22004 				// Event root wasn't found the use the root control
22005 				if (!eventRootCtrl) {
22006 					eventRootCtrl = parents[parents.length - 1] || self;
22007 				}
22008 
22009 				// Set the eventsRoot property on children that didn't have it
22010 				self._eventsRoot = eventRootCtrl;
22011 				for (l = i, i = 0; i < l; i++) {
22012 					parents[i]._eventsRoot = eventRootCtrl;
22013 				}
22014 
22015 				var eventRootDelegates = eventRootCtrl._delegates;
22016 				if (!eventRootDelegates) {
22017 					eventRootDelegates = eventRootCtrl._delegates = {};
22018 				}
22019 
22020 				// Bind native event delegates
22021 				for (name in nativeEvents) {
22022 					if (!nativeEvents) {
22023 						return false;
22024 					}
22025 
22026 					if (name === "wheel" && !hasWheelEventSupport) {
22027 						if (hasMouseWheelEventSupport) {
22028 							DomUtils.on(self.getEl(), "mousewheel", fixWheelEvent);
22029 						} else {
22030 							DomUtils.on(self.getEl(), "DOMMouseScroll", fixWheelEvent);
22031 						}
22032 
22033 						continue;
22034 					}
22035 
22036 					// Special treatment for mousenter/mouseleave since these doesn't bubble
22037 					if (name === "mouseenter" || name === "mouseleave") {
22038 						// Fake mousenter/mouseleave
22039 						if (!eventRootCtrl._hasMouseEnter) {
22040 							DomUtils.on(eventRootCtrl.getEl(), "mouseleave", mouseLeaveHandler);
22041 							DomUtils.on(eventRootCtrl.getEl(), "mouseover", mouseEnterHandler);
22042 							eventRootCtrl._hasMouseEnter = 1;
22043 						}
22044 					} else if (!eventRootDelegates[name]) {
22045 						DomUtils.on(eventRootCtrl.getEl(), name, delegate);
22046 						eventRootDelegates[name] = true;
22047 					}
22048 
22049 					// Remove the event once it's bound
22050 					nativeEvents[name] = false;
22051 				}
22052 			}
22053 		},
22054 
22055 		getRoot: function() {
22056 			var ctrl = this, rootControl, parents = [];
22057 
22058 			while (ctrl) {
22059 				if (ctrl.rootControl) {
22060 					rootControl = ctrl.rootControl;
22061 					break;
22062 				}
22063 
22064 				parents.push(ctrl);
22065 				rootControl = ctrl;
22066 				ctrl = ctrl.parent();
22067 			}
22068 
22069 			if (!rootControl) {
22070 				rootControl = this;
22071 			}
22072 
22073 			var i = parents.length;
22074 			while (i--) {
22075 				parents[i].rootControl = rootControl;
22076 			}
22077 
22078 			return rootControl;
22079 		},
22080 
22081 		/**
22082 		 * Reflows the current control and it's parents.
22083 		 * This should be used after you for example append children to the current control so
22084 		 * that the layout managers know that they need to reposition everything.
22085 		 *
22086 		 * @example
22087 		 * container.append({type: 'button', text: 'My button'}).reflow();
22088 		 *
22089 		 * @method reflow
22090 		 * @return {tinymce.ui.Control} Current control instance.
22091 		 */
22092 		reflow: function() {
22093 			this.repaint();
22094 
22095 			return this;
22096 		}
22097 
22098 		/**
22099 		 * Sets/gets the parent container for the control.
22100 		 *
22101 		 * @method parent
22102 		 * @param {tinymce.ui.Container} parent Optional parent to set.
22103 		 * @return {tinymce.ui.Control} Parent control or the current control on a set action.
22104 		 */
22105 		// parent: function(parent) {} -- Generated
22106 
22107 		/**
22108 		 * Sets/gets the text for the control.
22109 		 *
22110 		 * @method text
22111 		 * @param {String} value Value to set to control.
22112 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
22113 		 */
22114 		// text: function(value) {} -- Generated
22115 
22116 		/**
22117 		 * Sets/gets the width for the control.
22118 		 *
22119 		 * @method width
22120 		 * @param {Number} value Value to set to control.
22121 		 * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
22122 		 */
22123 		// width: function(value) {} -- Generated
22124 
22125 		/**
22126 		 * Sets/gets the height for the control.
22127 		 *
22128 		 * @method height
22129 		 * @param {Number} value Value to set to control.
22130 		 * @return {Number/tinymce.ui.Control} Current control on a set operation or current value on a get.
22131 		 */
22132 		// height: function(value) {} -- Generated
22133 
22134 		/**
22135 		 * Sets/gets the disabled state on the control.
22136 		 *
22137 		 * @method disabled
22138 		 * @param {Boolean} state Value to set to control.
22139 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
22140 		 */
22141 		// disabled: function(state) {} -- Generated
22142 
22143 		/**
22144 		 * Sets/gets the active for the control.
22145 		 *
22146 		 * @method active
22147 		 * @param {Boolean} state Value to set to control.
22148 		 * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get.
22149 		 */
22150 		// active: function(state) {} -- Generated
22151 
22152 		/**
22153 		 * Sets/gets the name for the control.
22154 		 *
22155 		 * @method name
22156 		 * @param {String} value Value to set to control.
22157 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
22158 		 */
22159 		// name: function(value) {} -- Generated
22160 
22161 		/**
22162 		 * Sets/gets the title for the control.
22163 		 *
22164 		 * @method title
22165 		 * @param {String} value Value to set to control.
22166 		 * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get.
22167 		 */
22168 		// title: function(value) {} -- Generated
22169 	});
22170 
22171 	return Control;
22172 });
22173 
22174 // Included from: js/tinymce/classes/ui/Factory.js
22175 
22176 /**
22177  * Factory.js
22178  *
22179  * Copyright, Moxiecode Systems AB
22180  * Released under LGPL License.
22181  *
22182  * License: http://www.tinymce.com/license
22183  * Contributing: http://www.tinymce.com/contributing
22184  */
22185 
22186 /*global tinymce:true */
22187 
22188 /**
22189  * This class is a factory for control instances. This enables you
22190  * to create instances of controls without having to require the UI controls directly.
22191  *
22192  * It also allow you to override or add new control types.
22193  *
22194  * @class tinymce.ui.Factory
22195  */
22196 define("tinymce/ui/Factory", [], function() {
22197 	"use strict";
22198 
22199 	var types = {}, namespaceInit;
22200 
22201 	return {
22202 		/**
22203 		 * Adds a new control instance type to the factory.
22204 		 *
22205 		 * @method add
22206 		 * @param {String} type Type name for example "button".
22207 		 * @param {function} typeClass Class type function.
22208 		 */
22209 		add: function(type, typeClass) {
22210 			types[type.toLowerCase()] = typeClass;
22211 		},
22212 
22213 		/**
22214 		 * Returns true/false if the specified type exists or not.
22215 		 *
22216 		 * @method has
22217 		 * @param {String} type Type to look for.
22218 		 * @return {Boolean} true/false if the control by name exists.
22219 		 */
22220 		has: function(type) {
22221 			return !!types[type.toLowerCase()];
22222 		},
22223 
22224 		/**
22225 		 * Creates a new control instance based on the settings provided. The instance created will be
22226 		 * based on the specified type property it can also create whole structures of components out of
22227 		 * the specified JSON object.
22228 		 *
22229 		 * @example
22230 		 * tinymce.ui.Factory.create({
22231 		 *     type: 'button',
22232 		 *     text: 'Hello world!'
22233 		 * });
22234 		 *
22235 		 * @method create
22236 		 * @param {Object/String} settings Name/Value object with items used to create the type.
22237 		 * @return {tinymce.ui.Control} Control instance based on the specified type.
22238 		 */
22239 		create: function(type, settings) {
22240 			var ControlType, name, namespace;
22241 
22242 			// Build type lookup
22243 			if (!namespaceInit) {
22244 				namespace = tinymce.ui;
22245 
22246 				for (name in namespace) {
22247 					types[name.toLowerCase()] = namespace[name];
22248 				}
22249 
22250 				namespaceInit = true;
22251 			}
22252 
22253 			// If string is specified then use it as the type
22254 			if (typeof(type) == 'string') {
22255 				settings = settings || {};
22256 				settings.type = type;
22257 			} else {
22258 				settings = type;
22259 				type = settings.type;
22260 			}
22261 
22262 			// Find control type
22263 			type = type.toLowerCase();
22264 			ControlType = types[type];
22265 
22266 			// #if debug
22267 
22268 			if (!ControlType) {
22269 				throw new Error("Could not find control by type: " + type);
22270 			}
22271 
22272 			// #endif
22273 
22274 			ControlType = new ControlType(settings);
22275 			ControlType.type = type; // Set the type on the instance, this will be used by the Selector engine
22276 
22277 			return ControlType;
22278 		}
22279 	};
22280 });
22281 
22282 // Included from: js/tinymce/classes/ui/KeyboardNavigation.js
22283 
22284 /**
22285  * KeyboardNavigation.js
22286  *
22287  * Copyright, Moxiecode Systems AB
22288  * Released under LGPL License.
22289  *
22290  * License: http://www.tinymce.com/license
22291  * Contributing: http://www.tinymce.com/contributing
22292  */
22293 
22294 /**
22295  * This class handles keyboard navigation of controls and elements.
22296  *
22297  * @class tinymce.ui.KeyboardNavigation
22298  */
22299 define("tinymce/ui/KeyboardNavigation", [
22300 ], function() {
22301 	"use strict";
22302 
22303 	/**
22304 	 * This class handles all keyboard navigation for WAI-ARIA support. Each root container
22305 	 * gets an instance of this class.
22306 	 *
22307 	 * @constructor
22308 	 */
22309 	return function(settings) {
22310 		var root = settings.root, focusedElement, focusedControl;
22311 
22312 		focusedElement = document.activeElement;
22313 		focusedControl = root.getParentCtrl(focusedElement);
22314 
22315 		/**
22316 		 * Returns the currently focused elements wai aria role of the currently
22317 		 * focused element or specified element.
22318 		 *
22319 		 * @private
22320 		 * @param {Element} elm Optional element to get role from.
22321 		 * @return {String} Role of specified element.
22322 		 */
22323 		function getRole(elm) {
22324 			elm = elm || focusedElement;
22325 
22326 			return elm && elm.getAttribute('role');
22327 		}
22328 
22329 		/**
22330 		 * Returns the wai role of the parent element of the currently
22331 		 * focused element or specified element.
22332 		 *
22333 		 * @private
22334 		 * @param {Element} elm Optional element to get parent role from.
22335 		 * @return {String} Role of the first parent that has a role.
22336 		 */
22337 		function getParentRole(elm) {
22338 			var role, parent = elm || focusedElement;
22339 
22340 			while ((parent = parent.parentNode)) {
22341 				if ((role = getRole(parent))) {
22342 					return role;
22343 				}
22344 			}
22345 		}
22346 
22347 		/**
22348 		 * Returns a wai aria property by name for example aria-selected.
22349 		 *
22350 		 * @private
22351 		 * @param {String} name Name of the aria property to get for example "disabled".
22352 		 * @return {String} Aria property value.
22353 		 */
22354 		function getAriaProp(name) {
22355 			var elm = focusedElement;
22356 
22357 			if (elm) {
22358 				return elm.getAttribute('aria-' + name);
22359 			}
22360 		}
22361 
22362 		/**
22363 		 * Is the element a text input element or not.
22364 		 *
22365 		 * @private
22366 		 * @param {Element} elm Element to check if it's an text input element or not.
22367 		 * @return {Boolean} True/false if the element is a text element or not.
22368 		 */
22369 		function isTextInputElement(elm) {
22370 			var tagName = elm.tagName.toUpperCase();
22371 
22372 			// Notice: since type can be "email" etc we don't check the type
22373 			// So all input elements gets treated as text input elements
22374 			return tagName == "INPUT" || tagName == "TEXTAREA";
22375 		}
22376 
22377 		/**
22378 		 * Returns true/false if the specified element can be focused or not.
22379 		 *
22380 		 * @private
22381 		 * @param {Element} elm DOM element to check if it can be focused or not.
22382 		 * @return {Boolean} True/false if the element can have focus.
22383 		 */
22384 		function canFocus(elm) {
22385 			if (isTextInputElement(elm) && !elm.hidden) {
22386 				return true;
22387 			}
22388 
22389 			if (/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell)$/.test(getRole(elm))) {
22390 				return true;
22391 			}
22392 
22393 			return false;
22394 		}
22395 
22396 		/**
22397 		 * Returns an array of focusable visible elements within the specified container element.
22398 		 *
22399 		 * @private
22400 		 * @param {Element} elm DOM element to find focusable elements within.
22401 		 * @return {Array} Array of focusable elements.
22402 		 */
22403 		function getFocusElements(elm) {
22404 			var elements = [];
22405 
22406 			function collect(elm) {
22407 				if (elm.nodeType != 1 || elm.style.display == 'none') {
22408 					return;
22409 				}
22410 
22411 				if (canFocus(elm)) {
22412 					elements.push(elm);
22413 				}
22414 
22415 				for (var i = 0; i < elm.childNodes.length; i++) {
22416 					collect(elm.childNodes[i]);
22417 				}
22418 			}
22419 
22420 			collect(elm || root.getEl());
22421 
22422 			return elements;
22423 		}
22424 
22425 		/**
22426 		 * Returns the navigation root control for the specified control. The navigation root
22427 		 * is the control that the keyboard navigation gets scoped to for example a menubar or toolbar group.
22428 		 * It will look for parents of the specified target control or the currenty focused control if this option is omitted.
22429 		 *
22430 		 * @private
22431 		 * @param {tinymce.ui.Control} targetControl Optional target control to find root of.
22432 		 * @return {tinymce.ui.Control} Navigation root control.
22433 		 */
22434 		function getNavigationRoot(targetControl) {
22435 			var navigationRoot, controls;
22436 
22437 			targetControl = targetControl || focusedControl;
22438 			controls = targetControl.parents().toArray();
22439 			controls.unshift(targetControl);
22440 
22441 			for (var i = 0; i < controls.length; i++) {
22442 				navigationRoot = controls[i];
22443 
22444 				if (navigationRoot.settings.ariaRoot) {
22445 					break;
22446 				}
22447 			}
22448 
22449 			return navigationRoot;
22450 		}
22451 
22452 		/**
22453 		 * Focuses the first item in the specified targetControl element or the last aria index if the
22454 		 * navigation root has the ariaRemember option enabled.
22455 		 *
22456 		 * @private
22457 		 * @param {tinymce.ui.Control} targetControl Target control to focus the first item in.
22458 		 */
22459 		function focusFirst(targetControl) {
22460 			var navigationRoot = getNavigationRoot(targetControl);
22461 			var focusElements = getFocusElements(navigationRoot.getEl());
22462 
22463 			if (navigationRoot.settings.ariaRemember && "lastAriaIndex" in navigationRoot) {
22464 				moveFocusToIndex(navigationRoot.lastAriaIndex, focusElements);
22465 			} else {
22466 				moveFocusToIndex(0, focusElements);
22467 			}
22468 		}
22469 
22470 		/**
22471 		 * Moves the focus to the specified index within the elements list.
22472 		 * This will scope the index to the size of the element list if it changed.
22473 		 *
22474 		 * @private
22475 		 * @param {Number} idx Specified index to move to.
22476 		 * @param {Array} elements Array with dom elements to move focus within.
22477 		 * @return {Number} Input index or a changed index if it was out of range.
22478 		 */
22479 		function moveFocusToIndex(idx, elements) {
22480 			if (idx < 0) {
22481 				idx = elements.length - 1;
22482 			} else if (idx >= elements.length) {
22483 				idx = 0;
22484 			}
22485 
22486 			if (elements[idx]) {
22487 				elements[idx].focus();
22488 			}
22489 
22490 			return idx;
22491 		}
22492 
22493 		/**
22494 		 * Moves the focus forwards or backwards.
22495 		 *
22496 		 * @private
22497 		 * @param {Number} dir Direction to move in positive means forward, negative means backwards.
22498 		 * @param {Array} elements Optional array of elements to move within defaults to the current navigation roots elements.
22499 		 */
22500 		function moveFocus(dir, elements) {
22501 			var idx = -1, navigationRoot = getNavigationRoot();
22502 
22503 			elements = elements || getFocusElements(navigationRoot.getEl());
22504 
22505 			for (var i = 0; i < elements.length; i++) {
22506 				if (elements[i] === focusedElement) {
22507 					idx = i;
22508 				}
22509 			}
22510 
22511 			idx += dir;
22512 			navigationRoot.lastAriaIndex = moveFocusToIndex(idx, elements);
22513 		}
22514 
22515 		/**
22516 		 * Moves the focus to the left this is called by the left key.
22517 		 *
22518 		 * @private
22519 		 */
22520 		function left() {
22521 			var parentRole = getParentRole();
22522 
22523 			if (parentRole == "tablist") {
22524 				moveFocus(-1, getFocusElements(focusedElement.parentNode));
22525 			} else if (focusedControl.parent().submenu) {
22526 				cancel();
22527 			} else {
22528 				moveFocus(-1);
22529 			}
22530 		}
22531 
22532 		/**
22533 		 * Moves the focus to the right this is called by the right key.
22534 		 *
22535 		 * @private
22536 		 */
22537 		function right() {
22538 			var role = getRole(), parentRole = getParentRole();
22539 
22540 			if (parentRole == "tablist") {
22541 				moveFocus(1, getFocusElements(focusedElement.parentNode));
22542 			} else if (role == "menuitem" && parentRole == "menu" && getAriaProp('haspopup')) {
22543 				enter();
22544 			} else {
22545 				moveFocus(1);
22546 			}
22547 		}
22548 
22549 		/**
22550 		 * Moves the focus to the up this is called by the up key.
22551 		 *
22552 		 * @private
22553 		 */
22554 		function up() {
22555 			moveFocus(-1);
22556 		}
22557 
22558 		/**
22559 		 * Moves the focus to the up this is called by the down key.
22560 		 *
22561 		 * @private
22562 		 */
22563 		function down() {
22564 			var role = getRole(), parentRole = getParentRole();
22565 
22566 			if (role == "menuitem" && parentRole == "menubar") {
22567 				enter();
22568 			} else if (role == "button" && getAriaProp('haspopup')) {
22569 				enter({key: 'down'});
22570 			} else {
22571 				moveFocus(1);
22572 			}
22573 		}
22574 
22575 		/**
22576 		 * Moves the focus to the next item or previous item depending on shift key.
22577 		 *
22578 		 * @private
22579 		 * @param {DOMEvent} e DOM event object.
22580 		 */
22581 		function tab(e) {
22582 			var parentRole = getParentRole();
22583 
22584 			if (parentRole == "tablist") {
22585 				var elm = getFocusElements(focusedControl.getEl('body'))[0];
22586 
22587 				if (elm) {
22588 					elm.focus();
22589 				}
22590 			} else {
22591 				moveFocus(e.shiftKey ? -1 : 1);
22592 			}
22593 		}
22594 
22595 		/**
22596 		 * Calls the cancel event on the currently focused control. This is normally done using the Esc key.
22597 		 *
22598 		 * @private
22599 		 */
22600 		function cancel() {
22601 			focusedControl.fire('cancel');
22602 		}
22603 
22604 		/**
22605 		 * Calls the click event on the currently focused control. This is normally done using the Enter/Space keys.
22606 		 *
22607 		 * @private
22608 		 * @param {Object} aria Optional aria data to pass along with the enter event.
22609 		 */
22610 		function enter(aria) {
22611 			aria = aria || {};
22612 			focusedControl.fire('click', {target: focusedElement, aria: aria});
22613 		}
22614 
22615 		root.on('keydown', function(e) {
22616 			function handleNonTabOrEscEvent(e, handler) {
22617 				// Ignore non tab keys for text elements
22618 				if (isTextInputElement(focusedElement)) {
22619 					return;
22620 				}
22621 
22622 				if (handler(e) !== false) {
22623 					e.preventDefault();
22624 				}
22625 			}
22626 
22627 			if (e.isDefaultPrevented()) {
22628 				return;
22629 			}
22630 
22631 			switch (e.keyCode) {
22632 				case 37: // DOM_VK_LEFT
22633 					handleNonTabOrEscEvent(e, left);
22634 					break;
22635 
22636 				case 39: // DOM_VK_RIGHT
22637 					handleNonTabOrEscEvent(e, right);
22638 					break;
22639 
22640 				case 38: // DOM_VK_UP
22641 					handleNonTabOrEscEvent(e, up);
22642 					break;
22643 
22644 				case 40: // DOM_VK_DOWN
22645 					handleNonTabOrEscEvent(e, down);
22646 					break;
22647 
22648 				case 27: // DOM_VK_ESCAPE
22649 					cancel();
22650 					break;
22651 
22652 				case 14: // DOM_VK_ENTER
22653 				case 13: // DOM_VK_RETURN
22654 				case 32: // DOM_VK_SPACE
22655 					handleNonTabOrEscEvent(e, enter);
22656 					break;
22657 
22658 				case 9: // DOM_VK_TAB
22659 					if (tab(e) !== false) {
22660 						e.preventDefault();
22661 					}
22662 					break;
22663 			}
22664 		});
22665 
22666 		root.on('focusin', function(e) {
22667 			focusedElement = e.target;
22668 			focusedControl = e.control;
22669 		});
22670 
22671 		return {
22672 			focusFirst: focusFirst
22673 		};
22674 	};
22675 });
22676 
22677 // Included from: js/tinymce/classes/ui/Container.js
22678 
22679 /**
22680  * Container.js
22681  *
22682  * Copyright, Moxiecode Systems AB
22683  * Released under LGPL License.
22684  *
22685  * License: http://www.tinymce.com/license
22686  * Contributing: http://www.tinymce.com/contributing
22687  */
22688 
22689 /**
22690  * Container control. This is extended by all controls that can have
22691  * children such as panels etc. You can also use this class directly as an
22692  * generic container instance. The container doesn't have any specific role or style.
22693  *
22694  * @-x-less Container.less
22695  * @class tinymce.ui.Container
22696  * @extends tinymce.ui.Control
22697  */
22698 define("tinymce/ui/Container", [
22699 	"tinymce/ui/Control",
22700 	"tinymce/ui/Collection",
22701 	"tinymce/ui/Selector",
22702 	"tinymce/ui/Factory",
22703 	"tinymce/ui/KeyboardNavigation",
22704 	"tinymce/util/Tools",
22705 	"tinymce/ui/DomUtils"
22706 ], function(Control, Collection, Selector, Factory, KeyboardNavigation, Tools, DomUtils) {
22707 	"use strict";
22708 
22709 	var selectorCache = {};
22710 
22711 	return Control.extend({
22712 		layout: '',
22713 		innerClass: 'container-inner',
22714 
22715 		/**
22716 		 * Constructs a new control instance with the specified settings.
22717 		 *
22718 		 * @constructor
22719 		 * @param {Object} settings Name/value object with settings.
22720 		 * @setting {Array} items Items to add to container in JSON format or control instances.
22721 		 * @setting {String} layout Layout manager by name to use.
22722 		 * @setting {Object} defaults Default settings to apply to all items.
22723 		 */
22724 		init: function(settings) {
22725 			var self = this;
22726 
22727 			self._super(settings);
22728 			settings = self.settings;
22729 			self._fixed = settings.fixed;
22730 			self._items = new Collection();
22731 
22732 			if (self.isRtl()) {
22733 				self.addClass('rtl');
22734 			}
22735 
22736 			self.addClass('container');
22737 			self.addClass('container-body', 'body');
22738 
22739 			if (settings.containerCls) {
22740 				self.addClass(settings.containerCls);
22741 			}
22742 
22743 			self._layout = Factory.create((settings.layout || self.layout) + 'layout');
22744 
22745 			if (self.settings.items) {
22746 				self.add(self.settings.items);
22747 			}
22748 
22749 			// TODO: Fix this!
22750 			self._hasBody = true;
22751 		},
22752 
22753 		/**
22754 		 * Returns a collection of child items that the container currently have.
22755 		 *
22756 		 * @method items
22757 		 * @return {tinymce.ui.Collection} Control collection direct child controls.
22758 		 */
22759 		items: function() {
22760 			return this._items;
22761 		},
22762 
22763 		/**
22764 		 * Find child controls by selector.
22765 		 *
22766 		 * @method find
22767 		 * @param {String} selector Selector CSS pattern to find children by.
22768 		 * @return {tinymce.ui.Collection} Control collection with child controls.
22769 		 */
22770 		find: function(selector) {
22771 			selector = selectorCache[selector] = selectorCache[selector] || new Selector(selector);
22772 
22773 			return selector.find(this);
22774 		},
22775 
22776 		/**
22777 		 * Adds one or many items to the current container. This will create instances of
22778 		 * the object representations if needed.
22779 		 *
22780 		 * @method add
22781 		 * @param {Array/Object/tinymce.ui.Control} items Array or item that will be added to the container.
22782 		 * @return {tinymce.ui.Collection} Current collection control.
22783 		 */
22784 		add: function(items) {
22785 			var self = this;
22786 
22787 			self.items().add(self.create(items)).parent(self);
22788 
22789 			return self;
22790 		},
22791 
22792 		/**
22793 		 * Focuses the current container instance. This will look
22794 		 * for the first control in the container and focus that.
22795 		 *
22796 		 * @method focus
22797 		 * @param {Boolean} keyboard Optional true/false if the focus was a keyboard focus or not.
22798 		 * @return {tinymce.ui.Collection} Current instance.
22799 		 */
22800 		focus: function(keyboard) {
22801 			var self = this, focusCtrl, keyboardNav, items;
22802 
22803 			if (keyboard) {
22804 				keyboardNav = self.keyboardNav || self.parents().eq(-1)[0].keyboardNav;
22805 
22806 				if (keyboardNav) {
22807 					keyboardNav.focusFirst(self);
22808 					return;
22809 				}
22810 			}
22811 
22812 			items = self.find('*');
22813 
22814 			// TODO: Figure out a better way to auto focus alert dialog buttons
22815 			if (self.statusbar) {
22816 				items.add(self.statusbar.items());
22817 			}
22818 
22819 			items.each(function(ctrl) {
22820 				if (ctrl.settings.autofocus) {
22821 					focusCtrl = null;
22822 					return false;
22823 				}
22824 
22825 				if (ctrl.canFocus) {
22826 					focusCtrl = focusCtrl || ctrl;
22827 				}
22828 			});
22829 
22830 			if (focusCtrl) {
22831 				focusCtrl.focus();
22832 			}
22833 
22834 			return self;
22835 		},
22836 
22837 		/**
22838 		 * Replaces the specified child control with a new control.
22839 		 *
22840 		 * @method replace
22841 		 * @param {tinymce.ui.Control} oldItem Old item to be replaced.
22842 		 * @param {tinymce.ui.Control} newItem New item to be inserted.
22843 		 */
22844 		replace: function(oldItem, newItem) {
22845 			var ctrlElm, items = this.items(), i = items.length;
22846 
22847 			// Replace the item in collection
22848 			while (i--) {
22849 				if (items[i] === oldItem) {
22850 					items[i] = newItem;
22851 					break;
22852 				}
22853 			}
22854 
22855 			if (i >= 0) {
22856 				// Remove new item from DOM
22857 				ctrlElm = newItem.getEl();
22858 				if (ctrlElm) {
22859 					ctrlElm.parentNode.removeChild(ctrlElm);
22860 				}
22861 
22862 				// Remove old item from DOM
22863 				ctrlElm = oldItem.getEl();
22864 				if (ctrlElm) {
22865 					ctrlElm.parentNode.removeChild(ctrlElm);
22866 				}
22867 			}
22868 
22869 			// Adopt the item
22870 			newItem.parent(this);
22871 		},
22872 
22873 		/**
22874 		 * Creates the specified items. If any of the items is plain JSON style objects
22875 		 * it will convert these into real tinymce.ui.Control instances.
22876 		 *
22877 		 * @method create
22878 		 * @param {Array} items Array of items to convert into control instances.
22879 		 * @return {Array} Array with control instances.
22880 		 */
22881 		create: function(items) {
22882 			var self = this, settings, ctrlItems = [];
22883 
22884 			// Non array structure, then force it into an array
22885 			if (!Tools.isArray(items)) {
22886 				items = [items];
22887 			}
22888 
22889 			// Add default type to each child control
22890 			Tools.each(items, function(item) {
22891 				if (item) {
22892 					// Construct item if needed
22893 					if (!(item instanceof Control)) {
22894 						// Name only then convert it to an object
22895 						if (typeof(item) == "string") {
22896 							item = {type: item};
22897 						}
22898 
22899 						// Create control instance based on input settings and default settings
22900 						settings = Tools.extend({}, self.settings.defaults, item);
22901 						item.type = settings.type = settings.type || item.type || self.settings.defaultType ||
22902 							(settings.defaults ? settings.defaults.type : null);
22903 						item = Factory.create(settings);
22904 					}
22905 
22906 					ctrlItems.push(item);
22907 				}
22908 			});
22909 
22910 			return ctrlItems;
22911 		},
22912 
22913 		/**
22914 		 * Renders new control instances.
22915 		 *
22916 		 * @private
22917 		 */
22918 		renderNew: function() {
22919 			var self = this;
22920 
22921 			// Render any new items
22922 			self.items().each(function(ctrl, index) {
22923 				var containerElm, fragment;
22924 
22925 				ctrl.parent(self);
22926 
22927 				if (!ctrl._rendered) {
22928 					containerElm = self.getEl('body');
22929 					fragment = DomUtils.createFragment(ctrl.renderHtml());
22930 
22931 					// Insert or append the item
22932 					if (containerElm.hasChildNodes() && index <= containerElm.childNodes.length - 1) {
22933 						containerElm.insertBefore(fragment, containerElm.childNodes[index]);
22934 					} else {
22935 						containerElm.appendChild(fragment);
22936 					}
22937 
22938 					ctrl.postRender();
22939 				}
22940 			});
22941 
22942 			self._layout.applyClasses(self);
22943 			self._lastRect = null;
22944 
22945 			return self;
22946 		},
22947 
22948 		/**
22949 		 * Appends new instances to the current container.
22950 		 *
22951 		 * @method append
22952 		 * @param {Array/tinymce.ui.Collection} items Array if controls to append.
22953 		 * @return {tinymce.ui.Container} Current container instance.
22954 		 */
22955 		append: function(items) {
22956 			return this.add(items).renderNew();
22957 		},
22958 
22959 		/**
22960 		 * Prepends new instances to the current container.
22961 		 *
22962 		 * @method prepend
22963 		 * @param {Array/tinymce.ui.Collection} items Array if controls to prepend.
22964 		 * @return {tinymce.ui.Container} Current container instance.
22965 		 */
22966 		prepend: function(items) {
22967 			var self = this;
22968 
22969 			self.items().set(self.create(items).concat(self.items().toArray()));
22970 
22971 			return self.renderNew();
22972 		},
22973 
22974 		/**
22975 		 * Inserts an control at a specific index.
22976 		 *
22977 		 * @method insert
22978 		 * @param {Array/tinymce.ui.Collection} items Array if controls to insert.
22979 		 * @param {Number} index Index to insert controls at.
22980 		 * @param {Boolean} [before=false] Inserts controls before the index.
22981 		 */
22982 		insert: function(items, index, before) {
22983 			var self = this, curItems, beforeItems, afterItems;
22984 
22985 			items = self.create(items);
22986 			curItems = self.items();
22987 
22988 			if (!before && index < curItems.length - 1) {
22989 				index += 1;
22990 			}
22991 
22992 			if (index >= 0 && index < curItems.length) {
22993 				beforeItems = curItems.slice(0, index).toArray();
22994 				afterItems = curItems.slice(index).toArray();
22995 				curItems.set(beforeItems.concat(items, afterItems));
22996 			}
22997 
22998 			return self.renderNew();
22999 		},
23000 
23001 		/**
23002 		 * Populates the form fields from the specified JSON data object.
23003 		 *
23004 		 * Control items in the form that matches the data will have it's value set.
23005 		 *
23006 		 * @method fromJSON
23007 		 * @param {Object} data JSON data object to set control values by.
23008 		 * @return {tinymce.ui.Container} Current form instance.
23009 		 */
23010 		fromJSON: function(data) {
23011 			var self = this;
23012 
23013 			for (var name in data) {
23014 				self.find('#' + name).value(data[name]);
23015 			}
23016 
23017 			return self;
23018 		},
23019 
23020 		/**
23021 		 * Serializes the form into a JSON object by getting all items
23022 		 * that has a name and a value.
23023 		 *
23024 		 * @method toJSON
23025 		 * @return {Object} JSON object with form data.
23026 		 */
23027 		toJSON: function() {
23028 			var self = this, data = {};
23029 
23030 			self.find('*').each(function(ctrl) {
23031 				var name = ctrl.name(), value = ctrl.value();
23032 
23033 				if (name && typeof(value) != "undefined") {
23034 					data[name] = value;
23035 				}
23036 			});
23037 
23038 			return data;
23039 		},
23040 
23041 		preRender: function() {
23042 		},
23043 
23044 		/**
23045 		 * Renders the control as a HTML string.
23046 		 *
23047 		 * @method renderHtml
23048 		 * @return {String} HTML representing the control.
23049 		 */
23050 		renderHtml: function() {
23051 			var self = this, layout = self._layout, role = this.settings.role;
23052 
23053 			self.preRender();
23054 			layout.preRender(self);
23055 
23056 			return (
23057 				'<div id="' + self._id + '" class="' + self.classes() + '"' + (role ? ' role="' + this.settings.role + '"' : '') + '>' +
23058 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
23059 						(self.settings.html || '') + layout.renderHtml(self) +
23060 					'</div>' +
23061 				'</div>'
23062 			);
23063 		},
23064 
23065 		/**
23066 		 * Post render method. Called after the control has been rendered to the target.
23067 		 *
23068 		 * @method postRender
23069 		 * @return {tinymce.ui.Container} Current combobox instance.
23070 		 */
23071 		postRender: function() {
23072 			var self = this, box;
23073 
23074 			self.items().exec('postRender');
23075 			self._super();
23076 
23077 			self._layout.postRender(self);
23078 			self._rendered = true;
23079 
23080 			if (self.settings.style) {
23081 				DomUtils.css(self.getEl(), self.settings.style);
23082 			}
23083 
23084 			if (self.settings.border) {
23085 				box = self.borderBox();
23086 				DomUtils.css(self.getEl(), {
23087 					'border-top-width': box.top,
23088 					'border-right-width': box.right,
23089 					'border-bottom-width': box.bottom,
23090 					'border-left-width': box.left
23091 				});
23092 			}
23093 
23094 			if (!self.parent()) {
23095 				self.keyboardNav = new KeyboardNavigation({
23096 					root: self
23097 				});
23098 			}
23099 
23100 			return self;
23101 		},
23102 
23103 		/**
23104 		 * Initializes the current controls layout rect.
23105 		 * This will be executed by the layout managers to determine the
23106 		 * default minWidth/minHeight etc.
23107 		 *
23108 		 * @method initLayoutRect
23109 		 * @return {Object} Layout rect instance.
23110 		 */
23111 		initLayoutRect: function() {
23112 			var self = this, layoutRect = self._super();
23113 
23114 			// Recalc container size by asking layout manager
23115 			self._layout.recalc(self);
23116 
23117 			return layoutRect;
23118 		},
23119 
23120 		/**
23121 		 * Recalculates the positions of the controls in the current container.
23122 		 * This is invoked by the reflow method and shouldn't be called directly.
23123 		 *
23124 		 * @method recalc
23125 		 */
23126 		recalc: function() {
23127 			var self = this, rect = self._layoutRect, lastRect = self._lastRect;
23128 
23129 			if (!lastRect || lastRect.w != rect.w || lastRect.h != rect.h) {
23130 				self._layout.recalc(self);
23131 				rect = self.layoutRect();
23132 				self._lastRect = {x: rect.x, y: rect.y, w: rect.w, h: rect.h};
23133 				return true;
23134 			}
23135 		},
23136 
23137 		/**
23138 		 * Reflows the current container and it's children and possible parents.
23139 		 * This should be used after you for example append children to the current control so
23140 		 * that the layout managers know that they need to reposition everything.
23141 		 *
23142 		 * @example
23143 		 * container.append({type: 'button', text: 'My button'}).reflow();
23144 		 *
23145 		 * @method reflow
23146 		 * @return {tinymce.ui.Container} Current container instance.
23147 		 */
23148 		reflow: function() {
23149 			var i;
23150 
23151 			if (this.visible()) {
23152 				Control.repaintControls = [];
23153 				Control.repaintControls.map = {};
23154 
23155 				this.recalc();
23156 				i = Control.repaintControls.length;
23157 
23158 				while (i--) {
23159 					Control.repaintControls[i].repaint();
23160 				}
23161 
23162 				// TODO: Fix me!
23163 				if (this.settings.layout !== "flow" && this.settings.layout !== "stack") {
23164 					this.repaint();
23165 				}
23166 
23167 				Control.repaintControls = [];
23168 			}
23169 
23170 			return this;
23171 		}
23172 	});
23173 });
23174 
23175 // Included from: js/tinymce/classes/ui/DragHelper.js
23176 
23177 /**
23178  * DragHelper.js
23179  *
23180  * Copyright, Moxiecode Systems AB
23181  * Released under LGPL License.
23182  *
23183  * License: http://www.tinymce.com/license
23184  * Contributing: http://www.tinymce.com/contributing
23185  */
23186 
23187 /**
23188  * Drag/drop helper class.
23189  *
23190  * @example
23191  * var dragHelper = new tinymce.ui.DragHelper('mydiv', {
23192  *     start: function(evt) {
23193  *     },
23194  *
23195  *     drag: function(evt) {
23196  *     },
23197  *
23198  *     end: function(evt) {
23199  *     }
23200  * });
23201  *
23202  * @class tinymce.ui.DragHelper
23203  */
23204 define("tinymce/ui/DragHelper", [
23205 	"tinymce/ui/DomUtils"
23206 ], function(DomUtils) {
23207 	"use strict";
23208 
23209 	function getDocumentSize() {
23210 		var doc = document, documentElement, body, scrollWidth, clientWidth;
23211 		var offsetWidth, scrollHeight, clientHeight, offsetHeight, max = Math.max;
23212 
23213 		documentElement = doc.documentElement;
23214 		body = doc.body;
23215 
23216 		scrollWidth = max(documentElement.scrollWidth, body.scrollWidth);
23217 		clientWidth = max(documentElement.clientWidth, body.clientWidth);
23218 		offsetWidth = max(documentElement.offsetWidth, body.offsetWidth);
23219 
23220 		scrollHeight = max(documentElement.scrollHeight, body.scrollHeight);
23221 		clientHeight = max(documentElement.clientHeight, body.clientHeight);
23222 		offsetHeight = max(documentElement.offsetHeight, body.offsetHeight);
23223 
23224 		return {
23225 			width: scrollWidth < offsetWidth ? clientWidth : scrollWidth,
23226 			height: scrollHeight < offsetHeight ? clientHeight : scrollHeight
23227 		};
23228 	}
23229 
23230 	return function(id, settings) {
23231 		var eventOverlayElm, doc = document, downButton, start, stop, drag, startX, startY;
23232 
23233 		settings = settings || {};
23234 
23235 		function getHandleElm() {
23236 			return doc.getElementById(settings.handle || id);
23237 		}
23238 
23239 		start = function(e) {
23240 			var docSize = getDocumentSize(), handleElm, cursor;
23241 
23242 			e.preventDefault();
23243 			downButton = e.button;
23244 			handleElm = getHandleElm();
23245 			startX = e.screenX;
23246 			startY = e.screenY;
23247 
23248 			// Grab cursor from handle
23249 			if (window.getComputedStyle) {
23250 				cursor = window.getComputedStyle(handleElm, null).getPropertyValue("cursor");
23251 			} else {
23252 				cursor = handleElm.runtimeStyle.cursor;
23253 			}
23254 
23255 			// Create event overlay and add it to document
23256 			eventOverlayElm = doc.createElement('div');
23257 			DomUtils.css(eventOverlayElm, {
23258 				position: "absolute",
23259 				top: 0, left: 0,
23260 				width: docSize.width,
23261 				height: docSize.height,
23262 				zIndex: 0x7FFFFFFF,
23263 				opacity: 0.0001,
23264 				background: 'red',
23265 				cursor: cursor
23266 			});
23267 
23268 			doc.body.appendChild(eventOverlayElm);
23269 
23270 			// Bind mouse events
23271 			DomUtils.on(doc, 'mousemove', drag);
23272 			DomUtils.on(doc, 'mouseup', stop);
23273 
23274 			// Begin drag
23275 			settings.start(e);
23276 		};
23277 
23278 		drag = function(e) {
23279 			if (e.button !== downButton) {
23280 				return stop(e);
23281 			}
23282 
23283 			e.deltaX = e.screenX - startX;
23284 			e.deltaY = e.screenY - startY;
23285 
23286 			e.preventDefault();
23287 			settings.drag(e);
23288 		};
23289 
23290 		stop = function(e) {
23291 			DomUtils.off(doc, 'mousemove', drag);
23292 			DomUtils.off(doc, 'mouseup', stop);
23293 
23294 			eventOverlayElm.parentNode.removeChild(eventOverlayElm);
23295 
23296 			if (settings.stop) {
23297 				settings.stop(e);
23298 			}
23299 		};
23300 
23301 		/**
23302 		 * Destroys the drag/drop helper instance.
23303 		 *
23304 		 * @method destroy
23305 		 */
23306 		this.destroy = function() {
23307 			DomUtils.off(getHandleElm());
23308 		};
23309 
23310 		DomUtils.on(getHandleElm(), 'mousedown', start);
23311 	};
23312 });
23313 
23314 // Included from: js/tinymce/classes/ui/Scrollable.js
23315 
23316 /**
23317  * Scrollable.js
23318  *
23319  * Copyright, Moxiecode Systems AB
23320  * Released under LGPL License.
23321  *
23322  * License: http://www.tinymce.com/license
23323  * Contributing: http://www.tinymce.com/contributing
23324  */
23325 
23326 /**
23327  * This mixin makes controls scrollable using custom scrollbars.
23328  *
23329  * @-x-less Scrollable.less
23330  * @mixin tinymce.ui.Scrollable
23331  */
23332 define("tinymce/ui/Scrollable", [
23333 	"tinymce/ui/DomUtils",
23334 	"tinymce/ui/DragHelper"
23335 ], function(DomUtils, DragHelper) {
23336 	"use strict";
23337 
23338 	return {
23339 		init: function() {
23340 			var self = this;
23341 			self.on('repaint', self.renderScroll);
23342 		},
23343 
23344 		renderScroll: function() {
23345 			var self = this, margin = 2;
23346 
23347 			function repaintScroll() {
23348 				var hasScrollH, hasScrollV, bodyElm;
23349 
23350 				function repaintAxis(axisName, posName, sizeName, contentSizeName, hasScroll, ax) {
23351 					var containerElm, scrollBarElm, scrollThumbElm;
23352 					var containerSize, scrollSize, ratio, rect;
23353 					var posNameLower, sizeNameLower;
23354 
23355 					scrollBarElm = self.getEl('scroll' + axisName);
23356 					if (scrollBarElm) {
23357 						posNameLower = posName.toLowerCase();
23358 						sizeNameLower = sizeName.toLowerCase();
23359 
23360 						if (self.getEl('absend')) {
23361 							DomUtils.css(self.getEl('absend'), posNameLower, self.layoutRect()[contentSizeName] - 1);
23362 						}
23363 
23364 						if (!hasScroll) {
23365 							DomUtils.css(scrollBarElm, 'display', 'none');
23366 							return;
23367 						}
23368 
23369 						DomUtils.css(scrollBarElm, 'display', 'block');
23370 						containerElm = self.getEl('body');
23371 						scrollThumbElm = self.getEl('scroll' + axisName + "t");
23372 						containerSize = containerElm["client" + sizeName] - (margin * 2);
23373 						containerSize -= hasScrollH && hasScrollV ? scrollBarElm["client" + ax] : 0;
23374 						scrollSize = containerElm["scroll" + sizeName];
23375 						ratio = containerSize / scrollSize;
23376 
23377 						rect = {};
23378 						rect[posNameLower] = containerElm["offset" + posName] + margin;
23379 						rect[sizeNameLower] = containerSize;
23380 						DomUtils.css(scrollBarElm, rect);
23381 
23382 						rect = {};
23383 						rect[posNameLower] = containerElm["scroll" + posName] * ratio;
23384 						rect[sizeNameLower] = containerSize * ratio;
23385 						DomUtils.css(scrollThumbElm, rect);
23386 					}
23387 				}
23388 
23389 				bodyElm = self.getEl('body');
23390 				hasScrollH = bodyElm.scrollWidth > bodyElm.clientWidth;
23391 				hasScrollV = bodyElm.scrollHeight > bodyElm.clientHeight;
23392 
23393 				repaintAxis("h", "Left", "Width", "contentW", hasScrollH, "Height");
23394 				repaintAxis("v", "Top", "Height", "contentH", hasScrollV, "Width");
23395 			}
23396 
23397 			function addScroll() {
23398 				function addScrollAxis(axisName, posName, sizeName, deltaPosName, ax) {
23399 					var scrollStart, axisId = self._id + '-scroll' + axisName, prefix = self.classPrefix;
23400 
23401 					self.getEl().appendChild(DomUtils.createFragment(
23402 						'<div id="' + axisId + '" class="' + prefix + 'scrollbar ' + prefix + 'scrollbar-' + axisName + '">' +
23403 							'<div id="' + axisId + 't" class="' + prefix + 'scrollbar-thumb"></div>' +
23404 						'</div>'
23405 					));
23406 
23407 					self.draghelper = new DragHelper(axisId + 't', {
23408 						start: function() {
23409 							scrollStart = self.getEl('body')["scroll" + posName];
23410 							DomUtils.addClass(DomUtils.get(axisId), prefix + 'active');
23411 						},
23412 
23413 						drag: function(e) {
23414 							var ratio, hasScrollH, hasScrollV, containerSize, layoutRect = self.layoutRect();
23415 
23416 							hasScrollH = layoutRect.contentW > layoutRect.innerW;
23417 							hasScrollV = layoutRect.contentH > layoutRect.innerH;
23418 							containerSize = self.getEl('body')["client" + sizeName] - (margin * 2);
23419 							containerSize -= hasScrollH && hasScrollV ? self.getEl('scroll' + axisName)["client" + ax] : 0;
23420 
23421 							ratio = containerSize / self.getEl('body')["scroll" + sizeName];
23422 							self.getEl('body')["scroll" + posName] = scrollStart + (e["delta" + deltaPosName] / ratio);
23423 						},
23424 
23425 						stop: function() {
23426 							DomUtils.removeClass(DomUtils.get(axisId), prefix + 'active');
23427 						}
23428 					});
23429 /*
23430 					self.on('click', function(e) {
23431 						if (e.target.id == self._id + '-scrollv') {
23432 
23433 						}
23434 					});*/
23435 				}
23436 
23437 				self.addClass('scroll');
23438 
23439 				addScrollAxis("v", "Top", "Height", "Y", "Width");
23440 				addScrollAxis("h", "Left", "Width", "X", "Height");
23441 			}
23442 
23443 			if (self.settings.autoScroll) {
23444 				if (!self._hasScroll) {
23445 					self._hasScroll = true;
23446 					addScroll();
23447 
23448 					self.on('wheel', function(e) {
23449 						var bodyEl = self.getEl('body');
23450 
23451 						bodyEl.scrollLeft += (e.deltaX || 0) * 10;
23452 						bodyEl.scrollTop += e.deltaY * 10;
23453 
23454 						repaintScroll();
23455 					});
23456 
23457 					DomUtils.on(self.getEl('body'), "scroll", repaintScroll);
23458 				}
23459 
23460 				repaintScroll();
23461 			}
23462 		}
23463 	};
23464 });
23465 
23466 // Included from: js/tinymce/classes/ui/Panel.js
23467 
23468 /**
23469  * Panel.js
23470  *
23471  * Copyright, Moxiecode Systems AB
23472  * Released under LGPL License.
23473  *
23474  * License: http://www.tinymce.com/license
23475  * Contributing: http://www.tinymce.com/contributing
23476  */
23477 
23478 /**
23479  * Creates a new panel.
23480  *
23481  * @-x-less Panel.less
23482  * @class tinymce.ui.Panel
23483  * @extends tinymce.ui.Container
23484  * @mixes tinymce.ui.Scrollable
23485  */
23486 define("tinymce/ui/Panel", [
23487 	"tinymce/ui/Container",
23488 	"tinymce/ui/Scrollable"
23489 ], function(Container, Scrollable) {
23490 	"use strict";
23491 
23492 	return Container.extend({
23493 		Defaults: {
23494 			layout: 'fit',
23495 			containerCls: 'panel'
23496 		},
23497 
23498 		Mixins: [Scrollable],
23499 
23500 		/**
23501 		 * Renders the control as a HTML string.
23502 		 *
23503 		 * @method renderHtml
23504 		 * @return {String} HTML representing the control.
23505 		 */
23506 		renderHtml: function() {
23507 			var self = this, layout = self._layout, innerHtml = self.settings.html;
23508 
23509 			self.preRender();
23510 			layout.preRender(self);
23511 
23512 			if (typeof(innerHtml) == "undefined") {
23513 				innerHtml = (
23514 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
23515 						layout.renderHtml(self) +
23516 					'</div>'
23517 				);
23518 			} else {
23519 				if (typeof(innerHtml) == 'function') {
23520 					innerHtml = innerHtml.call(self);
23521 				}
23522 
23523 				self._hasBody = false;
23524 			}
23525 
23526 			return (
23527 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1" role="group">' +
23528 					(self._preBodyHtml || '') +
23529 					innerHtml +
23530 				'</div>'
23531 			);
23532 		}
23533 	});
23534 });
23535 
23536 // Included from: js/tinymce/classes/ui/Movable.js
23537 
23538 /**
23539  * Movable.js
23540  *
23541  * Copyright, Moxiecode Systems AB
23542  * Released under LGPL License.
23543  *
23544  * License: http://www.tinymce.com/license
23545  * Contributing: http://www.tinymce.com/contributing
23546  */
23547 
23548 /**
23549  * Movable mixin. Makes controls movable absolute and relative to other elements.
23550  *
23551  * @mixin tinymce.ui.Movable
23552  */
23553 define("tinymce/ui/Movable", [
23554 	"tinymce/ui/DomUtils"
23555 ], function(DomUtils) {
23556 	"use strict";
23557 
23558 	function calculateRelativePosition(ctrl, targetElm, rel) {
23559 		var ctrlElm, pos, x, y, selfW, selfH, targetW, targetH, viewport, size;
23560 
23561 		viewport = DomUtils.getViewPort();
23562 
23563 		// Get pos of target
23564 		pos = DomUtils.getPos(targetElm);
23565 		x = pos.x;
23566 		y = pos.y;
23567 
23568 		if (ctrl._fixed) {
23569 			x -= viewport.x;
23570 			y -= viewport.y;
23571 		}
23572 
23573 		// Get size of self
23574 		ctrlElm = ctrl.getEl();
23575 		size = DomUtils.getSize(ctrlElm);
23576 		selfW = size.width;
23577 		selfH = size.height;
23578 
23579 		// Get size of target
23580 		size = DomUtils.getSize(targetElm);
23581 		targetW = size.width;
23582 		targetH = size.height;
23583 
23584 		// Parse align string
23585 		rel = (rel || '').split('');
23586 
23587 		// Target corners
23588 		if (rel[0] === 'b') {
23589 			y += targetH;
23590 		}
23591 
23592 		if (rel[1] === 'r') {
23593 			x += targetW;
23594 		}
23595 
23596 		if (rel[0] === 'c') {
23597 			y += Math.round(targetH / 2);
23598 		}
23599 
23600 		if (rel[1] === 'c') {
23601 			x += Math.round(targetW / 2);
23602 		}
23603 
23604 		// Self corners
23605 		if (rel[3] === 'b') {
23606 			y -= selfH;
23607 		}
23608 
23609 		if (rel[4] === 'r') {
23610 			x -= selfW;
23611 		}
23612 
23613 		if (rel[3] === 'c') {
23614 			y -= Math.round(selfH / 2);
23615 		}
23616 
23617 		if (rel[4] === 'c') {
23618 			x -= Math.round(selfW / 2);
23619 		}
23620 
23621 		return {
23622 			x: x,
23623 			y: y,
23624 			w: selfW,
23625 			h: selfH
23626 		};
23627 	}
23628 
23629 	return {
23630 		/**
23631 		 * Tests various positions to get the most suitable one.
23632 		 *
23633 		 * @method testMoveRel
23634 		 * @param {DOMElement} elm Element to position against.
23635 		 * @param {Array} rels Array with relative positions.
23636 		 * @return {String} Best suitable relative position.
23637 		 */
23638 		testMoveRel: function(elm, rels) {
23639 			var viewPortRect = DomUtils.getViewPort();
23640 
23641 			for (var i = 0; i < rels.length; i++) {
23642 				var pos = calculateRelativePosition(this, elm, rels[i]);
23643 
23644 				if (this._fixed) {
23645 					if (pos.x > 0 && pos.x + pos.w < viewPortRect.w && pos.y > 0 && pos.y + pos.h < viewPortRect.h) {
23646 						return rels[i];
23647 					}
23648 				} else {
23649 					if (pos.x > viewPortRect.x && pos.x + pos.w < viewPortRect.w + viewPortRect.x &&
23650 						pos.y > viewPortRect.y && pos.y + pos.h < viewPortRect.h + viewPortRect.y) {
23651 						return rels[i];
23652 					}
23653 				}
23654 			}
23655 
23656 			return rels[0];
23657 		},
23658 
23659 		/**
23660 		 * Move relative to the specified element.
23661 		 *
23662 		 * @method moveRel
23663 		 * @param {Element} elm Element to move relative to.
23664 		 * @param {String} rel Relative mode. For example: br-tl.
23665 		 * @return {tinymce.ui.Control} Current control instance.
23666 		 */
23667 		moveRel: function(elm, rel) {
23668 			if (typeof(rel) != 'string') {
23669 				rel = this.testMoveRel(elm, rel);
23670 			}
23671 
23672 			var pos = calculateRelativePosition(this, elm, rel);
23673 			return this.moveTo(pos.x, pos.y);
23674 		},
23675 
23676 		/**
23677 		 * Move by a relative x, y values.
23678 		 *
23679 		 * @method moveBy
23680 		 * @param {Number} dx Relative x position.
23681 		 * @param {Number} dy Relative y position.
23682 		 * @return {tinymce.ui.Control} Current control instance.
23683 		 */
23684 		moveBy: function(dx, dy) {
23685 			var self = this, rect = self.layoutRect();
23686 
23687 			self.moveTo(rect.x + dx, rect.y + dy);
23688 
23689 			return self;
23690 		},
23691 
23692 		/**
23693 		 * Move to absolute position.
23694 		 *
23695 		 * @method moveTo
23696 		 * @param {Number} x Absolute x position.
23697 		 * @param {Number} y Absolute y position.
23698 		 * @return {tinymce.ui.Control} Current control instance.
23699 		 */
23700 		moveTo: function(x, y) {
23701 			var self = this;
23702 
23703 			// TODO: Move this to some global class
23704 			function contrain(value, max, size) {
23705 				if (value < 0) {
23706 					return 0;
23707 				}
23708 
23709 				if (value + size > max) {
23710 					value = max - size;
23711 					return value < 0 ? 0 : value;
23712 				}
23713 
23714 				return value;
23715 			}
23716 
23717 			if (self.settings.constrainToViewport) {
23718 				var viewPortRect = DomUtils.getViewPort(window);
23719 				var layoutRect = self.layoutRect();
23720 
23721 				x = contrain(x, viewPortRect.w + viewPortRect.x, layoutRect.w);
23722 				y = contrain(y, viewPortRect.h + viewPortRect.y, layoutRect.h);
23723 			}
23724 
23725 			if (self._rendered) {
23726 				self.layoutRect({x: x, y: y}).repaint();
23727 			} else {
23728 				self.settings.x = x;
23729 				self.settings.y = y;
23730 			}
23731 
23732 			self.fire('move', {x: x, y: y});
23733 
23734 			return self;
23735 		}
23736 	};
23737 });
23738 
23739 // Included from: js/tinymce/classes/ui/Resizable.js
23740 
23741 /**
23742  * Resizable.js
23743  *
23744  * Copyright, Moxiecode Systems AB
23745  * Released under LGPL License.
23746  *
23747  * License: http://www.tinymce.com/license
23748  * Contributing: http://www.tinymce.com/contributing
23749  */
23750 
23751 /**
23752  * Resizable mixin. Enables controls to be resized.
23753  *
23754  * @mixin tinymce.ui.Resizable
23755  */
23756 define("tinymce/ui/Resizable", [
23757 	"tinymce/ui/DomUtils"
23758 ], function(DomUtils) {
23759 	"use strict";
23760 
23761 	return {
23762 		/** 
23763 		 * Resizes the control to contents.
23764 		 *
23765 		 * @method resizeToContent
23766 		 */
23767 		resizeToContent: function() {
23768 			this._layoutRect.autoResize = true;
23769 			this._lastRect = null;
23770 			this.reflow();
23771 		},
23772 
23773 		/** 
23774 		 * Resizes the control to a specific width/height.
23775 		 *
23776 		 * @method resizeTo
23777 		 * @param {Number} w Control width.
23778 		 * @param {Number} h Control height.
23779 		 * @return {tinymce.ui.Control} Current control instance.
23780 		 */
23781 		resizeTo: function(w, h) {
23782 			// TODO: Fix hack
23783 			if (w <= 1 || h <= 1) {
23784 				var rect = DomUtils.getWindowSize();
23785 
23786 				w = w <= 1 ? w * rect.w : w;
23787 				h = h <= 1 ? h * rect.h : h;
23788 			}
23789 
23790 			this._layoutRect.autoResize = false;
23791 			return this.layoutRect({minW: w, minH: h, w: w, h: h}).reflow();
23792 		},
23793 
23794 		/** 
23795 		 * Resizes the control to a specific relative width/height.
23796 		 *
23797 		 * @method resizeBy
23798 		 * @param {Number} dw Relative control width.
23799 		 * @param {Number} dh Relative control height.
23800 		 * @return {tinymce.ui.Control} Current control instance.
23801 		 */
23802 		resizeBy: function(dw, dh) {
23803 			var self = this, rect = self.layoutRect();
23804 
23805 			return self.resizeTo(rect.w + dw, rect.h + dh);
23806 		}
23807 	};
23808 });
23809 
23810 // Included from: js/tinymce/classes/ui/FloatPanel.js
23811 
23812 /**
23813  * FloatPanel.js
23814  *
23815  * Copyright, Moxiecode Systems AB
23816  * Released under LGPL License.
23817  *
23818  * License: http://www.tinymce.com/license
23819  * Contributing: http://www.tinymce.com/contributing
23820  */
23821 
23822 /**
23823  * This class creates a floating panel.
23824  *
23825  * @-x-less FloatPanel.less
23826  * @class tinymce.ui.FloatPanel
23827  * @extends tinymce.ui.Panel
23828  * @mixes tinymce.ui.Movable
23829  * @mixes tinymce.ui.Resizable
23830  */
23831 define("tinymce/ui/FloatPanel", [
23832 	"tinymce/ui/Panel",
23833 	"tinymce/ui/Movable",
23834 	"tinymce/ui/Resizable",
23835 	"tinymce/ui/DomUtils"
23836 ], function(Panel, Movable, Resizable, DomUtils) {
23837 	"use strict";
23838 
23839 	var documentClickHandler, documentScrollHandler, visiblePanels = [];
23840 	var zOrder = [], hasModal;
23841 
23842 	var FloatPanel = Panel.extend({
23843 		Mixins: [Movable, Resizable],
23844 
23845 		/**
23846 		 * Constructs a new control instance with the specified settings.
23847 		 *
23848 		 * @constructor
23849 		 * @param {Object} settings Name/value object with settings.
23850 		 * @setting {Boolean} autohide Automatically hide the panel.
23851 		 */
23852 		init: function(settings) {
23853 			var self = this;
23854 
23855 			function reorder() {
23856 				var i, zIndex = FloatPanel.zIndex || 0xFFFF, topModal;
23857 
23858 				if (zOrder.length) {
23859 					for (i = 0; i < zOrder.length; i++) {
23860 						if (zOrder[i].modal) {
23861 							zIndex++;
23862 							topModal = zOrder[i];
23863 						}
23864 
23865 						zOrder[i].getEl().style.zIndex = zIndex;
23866 						zOrder[i].zIndex = zIndex;
23867 						zIndex++;
23868 					}
23869 				}
23870 
23871 				var modalBlockEl = document.getElementById(self.classPrefix + 'modal-block');
23872 
23873 				if (topModal) {
23874 					DomUtils.css(modalBlockEl, 'z-index', topModal.zIndex - 1);
23875 				} else if (modalBlockEl) {
23876 					modalBlockEl.parentNode.removeChild(modalBlockEl);
23877 					hasModal = false;
23878 				}
23879 
23880 				FloatPanel.currentZIndex = zIndex;
23881 			}
23882 
23883 			function isChildOf(ctrl, parent) {
23884 				while (ctrl) {
23885 					if (ctrl == parent) {
23886 						return true;
23887 					}
23888 
23889 					ctrl = ctrl.parent();
23890 				}
23891 			}
23892 
23893 			/**
23894 			 * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
23895 			 * also reposition all child panels of the current panel.
23896 			 */
23897 			function repositionPanel(panel) {
23898 				var scrollY = DomUtils.getViewPort().y;
23899 
23900 				function toggleFixedChildPanels(fixed, deltaY) {
23901 					var parent;
23902 
23903 					for (var i = 0; i < visiblePanels.length; i++) {
23904 						if (visiblePanels[i] != panel) {
23905 							parent = visiblePanels[i].parent();
23906 
23907 							while (parent && (parent = parent.parent())) {
23908 								if (parent == panel) {
23909 									visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
23910 								}
23911 							}
23912 						}
23913 					}
23914 				}
23915 
23916 				if (panel.settings.autofix) {
23917 					if (!panel._fixed) {
23918 						panel._autoFixY = panel.layoutRect().y;
23919 
23920 						if (panel._autoFixY < scrollY) {
23921 							panel.fixed(true).layoutRect({y: 0}).repaint();
23922 							toggleFixedChildPanels(true, scrollY - panel._autoFixY);
23923 						}
23924 					} else {
23925 						if (panel._autoFixY > scrollY) {
23926 							panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
23927 							toggleFixedChildPanels(false, panel._autoFixY - scrollY);
23928 						}
23929 					}
23930 				}
23931 			}
23932 
23933 			self._super(settings);
23934 			self._eventsRoot = self;
23935 
23936 			self.addClass('floatpanel');
23937 
23938 			// Hide floatpanes on click out side the root button
23939 			if (settings.autohide) {
23940 				if (!documentClickHandler) {
23941 					documentClickHandler = function(e) {
23942 						// Hide any float panel when a click is out side that float panel and the
23943 						// float panels direct parent for example a click on a menu button
23944 						var i = visiblePanels.length;
23945 						while (i--) {
23946 							var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target);
23947 
23948 							if (panel.settings.autohide) {
23949 								if (clickCtrl) {
23950 									if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
23951 										continue;
23952 									}
23953 								}
23954 
23955 								e = panel.fire('autohide', {target: e.target});
23956 								if (!e.isDefaultPrevented()) {
23957 									panel.hide();
23958 								}
23959 							}
23960 						}
23961 					};
23962 
23963 					DomUtils.on(document, 'click', documentClickHandler);
23964 				}
23965 
23966 				visiblePanels.push(self);
23967 			}
23968 
23969 			if (settings.autofix) {
23970 				if (!documentScrollHandler) {
23971 					documentScrollHandler = function() {
23972 						var i;
23973 
23974 						i = visiblePanels.length;
23975 						while (i--) {
23976 							repositionPanel(visiblePanels[i]);
23977 						}
23978 					};
23979 
23980 					DomUtils.on(window, 'scroll', documentScrollHandler);
23981 				}
23982 
23983 				self.on('move', function() {
23984 					repositionPanel(this);
23985 				});
23986 			}
23987 
23988 			self.on('postrender show', function(e) {
23989 				if (e.control == self) {
23990 					var modalBlockEl, prefix = self.classPrefix;
23991 
23992 					if (self.modal && !hasModal) {
23993 						modalBlockEl = DomUtils.createFragment('<div id="' + prefix + 'modal-block" class="' +
23994 							prefix + 'reset ' + prefix + 'fade"></div>');
23995 						modalBlockEl = modalBlockEl.firstChild;
23996 
23997 						self.getContainerElm().appendChild(modalBlockEl);
23998 
23999 						setTimeout(function() {
24000 							DomUtils.addClass(modalBlockEl, prefix + 'in');
24001 							DomUtils.addClass(self.getEl(), prefix + 'in');
24002 						}, 0);
24003 
24004 						hasModal = true;
24005 					}
24006 
24007 					zOrder.push(self);
24008 					reorder();
24009 				}
24010 			});
24011 
24012 			self.on('close hide', function(e) {
24013 				if (e.control == self) {
24014 					var i = zOrder.length;
24015 
24016 					while (i--) {
24017 						if (zOrder[i] === self) {
24018 							zOrder.splice(i, 1);
24019 						}
24020 					}
24021 
24022 					reorder();
24023 				}
24024 			});
24025 
24026 			self.on('show', function() {
24027 				self.parents().each(function(ctrl) {
24028 					if (ctrl._fixed) {
24029 						self.fixed(true);
24030 						return false;
24031 					}
24032 				});
24033 			});
24034 
24035 			if (settings.popover) {
24036 				self._preBodyHtml = '<div class="' + self.classPrefix + 'arrow"></div>';
24037 				self.addClass('popover').addClass('bottom').addClass(self.isRtl() ? 'end' : 'start');
24038 			}
24039 		},
24040 
24041 		fixed: function(state) {
24042 			var self = this;
24043 
24044 			if (self._fixed != state) {
24045 				if (self._rendered) {
24046 					var viewport = DomUtils.getViewPort();
24047 
24048 					if (state) {
24049 						self.layoutRect().y -= viewport.y;
24050 					} else {
24051 						self.layoutRect().y += viewport.y;
24052 					}
24053 				}
24054 
24055 				self.toggleClass('fixed', state);
24056 				self._fixed = state;
24057 			}
24058 
24059 			return self;
24060 		},
24061 
24062 		/**
24063 		 * Shows the current float panel.
24064 		 *
24065 		 * @method show
24066 		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
24067 		 */
24068 		show: function() {
24069 			var self = this, i, state = self._super();
24070 
24071 			i = visiblePanels.length;
24072 			while (i--) {
24073 				if (visiblePanels[i] === self) {
24074 					break;
24075 				}
24076 			}
24077 
24078 			if (i === -1) {
24079 				visiblePanels.push(self);
24080 			}
24081 
24082 			return state;
24083 		},
24084 
24085 		/**
24086 		 * Hides the current float panel.
24087 		 *
24088 		 * @method hide
24089 		 * @return {tinymce.ui.FloatPanel} Current floatpanel instance.
24090 		 */
24091 		hide: function() {
24092 			removeVisiblePanel(this);
24093 			return this._super();
24094 		},
24095 
24096 		/**
24097 		 * Hides all visible the float panels.
24098 		 *
24099 		 * @method hideAll
24100 		 */
24101 		hideAll: function() {
24102 			FloatPanel.hideAll();
24103 		},
24104 
24105 		/**
24106 		 * Closes the float panel. This will remove the float panel from page and fire the close event.
24107 		 *
24108 		 * @method close
24109 		 */
24110 		close: function() {
24111 			var self = this;
24112 
24113 			self.fire('close');
24114 
24115 			return self.remove();
24116 		},
24117 
24118 		/**
24119 		 * Removes the float panel from page.
24120 		 *
24121 		 * @method remove
24122 		 */
24123 		remove: function() {
24124 			removeVisiblePanel(this);
24125 			this._super();
24126 		},
24127 
24128 		postRender: function() {
24129 			var self = this;
24130 
24131 			if (self.settings.bodyRole) {
24132 				this.getEl('body').setAttribute('role', self.settings.bodyRole);
24133 			}
24134 
24135 			return self._super();
24136 		}
24137 	});
24138 
24139 	/**
24140 	 * Hides all visible the float panels.
24141 	 *
24142 	 * @static
24143 	 * @method hideAll
24144 	 */
24145 	FloatPanel.hideAll = function() {
24146 		var i = visiblePanels.length;
24147 
24148 		while (i--) {
24149 			var panel = visiblePanels[i];
24150 
24151 			if (panel && panel.settings.autohide) {
24152 				panel.hide();
24153 				visiblePanels.splice(i, 1);
24154 			}
24155 		}
24156 	};
24157 
24158 	function removeVisiblePanel(panel) {
24159 		var i;
24160 
24161 		i = visiblePanels.length;
24162 		while (i--) {
24163 			if (visiblePanels[i] === panel) {
24164 				visiblePanels.splice(i, 1);
24165 			}
24166 		}
24167 
24168 		i = zOrder.length;
24169 		while (i--) {
24170 			if (zOrder[i] === panel) {
24171 				zOrder.splice(i, 1);
24172 			}
24173 		}
24174 	}
24175 
24176 	return FloatPanel;
24177 });
24178 
24179 // Included from: js/tinymce/classes/ui/Window.js
24180 
24181 /**
24182  * Window.js
24183  *
24184  * Copyright, Moxiecode Systems AB
24185  * Released under LGPL License.
24186  *
24187  * License: http://www.tinymce.com/license
24188  * Contributing: http://www.tinymce.com/contributing
24189  */
24190 
24191 /**
24192  * Creates a new window.
24193  *
24194  * @-x-less Window.less
24195  * @class tinymce.ui.Window
24196  * @extends tinymce.ui.FloatPanel
24197  */
24198 define("tinymce/ui/Window", [
24199 	"tinymce/ui/FloatPanel",
24200 	"tinymce/ui/Panel",
24201 	"tinymce/ui/DomUtils",
24202 	"tinymce/ui/DragHelper"
24203 ], function(FloatPanel, Panel, DomUtils, DragHelper) {
24204 	"use strict";
24205 
24206 	var Window = FloatPanel.extend({
24207 		modal: true,
24208 
24209 		Defaults: {
24210 			border: 1,
24211 			layout: 'flex',
24212 			containerCls: 'panel',
24213 			role: 'dialog',
24214 			callbacks: {
24215 				submit: function() {
24216 					this.fire('submit', {data: this.toJSON()});
24217 				},
24218 
24219 				close: function() {
24220 					this.close();
24221 				}
24222 			}
24223 		},
24224 
24225 		/**
24226 		 * Constructs a instance with the specified settings.
24227 		 *
24228 		 * @constructor
24229 		 * @param {Object} settings Name/value object with settings.
24230 		 */
24231 		init: function(settings) {
24232 			var self = this;
24233 
24234 			self._super(settings);
24235 
24236 			if (self.isRtl()) {
24237 				self.addClass('rtl');
24238 			}
24239 
24240 			self.addClass('window');
24241 			self._fixed = true;
24242 
24243 			// Create statusbar
24244 			if (settings.buttons) {
24245 				self.statusbar = new Panel({
24246 					layout: 'flex',
24247 					border: '1 0 0 0',
24248 					spacing: 3,
24249 					padding: 10,
24250 					align: 'center',
24251 					pack: self.isRtl() ? 'start' : 'end',
24252 					defaults: {
24253 						type: 'button'
24254 					},
24255 					items: settings.buttons
24256 				});
24257 
24258 				self.statusbar.addClass('foot');
24259 				self.statusbar.parent(self);
24260 			}
24261 
24262 			self.on('click', function(e) {
24263 				if (e.target.className.indexOf(self.classPrefix + 'close') != -1) {
24264 					self.close();
24265 				}
24266 			});
24267 
24268 			self.on('cancel', function() {
24269 				self.close();
24270 			});
24271 
24272 			self.aria('describedby', self.describedBy || self._id + '-none');
24273 			self.aria('label', settings.title);
24274 			self._fullscreen = false;
24275 		},
24276 
24277 		/**
24278 		 * Recalculates the positions of the controls in the current container.
24279 		 * This is invoked by the reflow method and shouldn't be called directly.
24280 		 *
24281 		 * @method recalc
24282 		 */
24283 		recalc: function() {
24284 			var self = this, statusbar = self.statusbar, layoutRect, width, x, needsRecalc;
24285 
24286 			if (self._fullscreen) {
24287 				self.layoutRect(DomUtils.getWindowSize());
24288 				self.layoutRect().contentH = self.layoutRect().innerH;
24289 			}
24290 
24291 			self._super();
24292 
24293 			layoutRect = self.layoutRect();
24294 
24295 			// Resize window based on title width
24296 			if (self.settings.title && !self._fullscreen) {
24297 				width = layoutRect.headerW;
24298 				if (width > layoutRect.w) {
24299 					x = layoutRect.x - Math.max(0, width / 2);
24300 					self.layoutRect({w: width, x: x});
24301 					needsRecalc = true;
24302 				}
24303 			}
24304 
24305 			// Resize window based on statusbar width
24306 			if (statusbar) {
24307 				statusbar.layoutRect({w: self.layoutRect().innerW}).recalc();
24308 
24309 				width = statusbar.layoutRect().minW + layoutRect.deltaW;
24310 				if (width > layoutRect.w) {
24311 					x = layoutRect.x - Math.max(0, width - layoutRect.w);
24312 					self.layoutRect({w: width, x: x});
24313 					needsRecalc = true;
24314 				}
24315 			}
24316 
24317 			// Recalc body and disable auto resize
24318 			if (needsRecalc) {
24319 				self.recalc();
24320 			}
24321 		},
24322 
24323 		/**
24324 		 * Initializes the current controls layout rect.
24325 		 * This will be executed by the layout managers to determine the
24326 		 * default minWidth/minHeight etc.
24327 		 *
24328 		 * @method initLayoutRect
24329 		 * @return {Object} Layout rect instance.
24330 		 */
24331 		initLayoutRect: function() {
24332 			var self = this, layoutRect = self._super(), deltaH = 0, headEl;
24333 
24334 			// Reserve vertical space for title
24335 			if (self.settings.title && !self._fullscreen) {
24336 				headEl = self.getEl('head');
24337 
24338 				var size = DomUtils.getSize(headEl);
24339 
24340 				layoutRect.headerW = size.width;
24341 				layoutRect.headerH = size.height;
24342 
24343 				deltaH += layoutRect.headerH;
24344 			}
24345 
24346 			// Reserve vertical space for statusbar
24347 			if (self.statusbar) {
24348 				deltaH += self.statusbar.layoutRect().h;
24349 			}
24350 
24351 			layoutRect.deltaH += deltaH;
24352 			layoutRect.minH += deltaH;
24353 			//layoutRect.innerH -= deltaH;
24354 			layoutRect.h += deltaH;
24355 
24356 			var rect = DomUtils.getWindowSize();
24357 
24358 			layoutRect.x = Math.max(0, rect.w / 2 - layoutRect.w / 2);
24359 			layoutRect.y = Math.max(0, rect.h / 2 - layoutRect.h / 2);
24360 
24361 			return layoutRect;
24362 		},
24363 
24364 		/**
24365 		 * Renders the control as a HTML string.
24366 		 *
24367 		 * @method renderHtml
24368 		 * @return {String} HTML representing the control.
24369 		 */
24370 		renderHtml: function() {
24371 			var self = this, layout = self._layout, id = self._id, prefix = self.classPrefix;
24372 			var settings = self.settings, headerHtml = '', footerHtml = '', html = settings.html;
24373 
24374 			self.preRender();
24375 			layout.preRender(self);
24376 
24377 			if (settings.title) {
24378 				headerHtml = (
24379 					'<div id="' + id + '-head" class="' + prefix + 'window-head">' +
24380 						'<div id="' + id + '-title" class="' + prefix + 'title">' + self.encode(settings.title) + '</div>' +
24381 						'<button type="button" class="' + prefix + 'close" aria-hidden="true">\u00d7</button>' +
24382 						'<div id="' + id + '-dragh" class="' + prefix + 'dragh"></div>' +
24383 					'</div>'
24384 				);
24385 			}
24386 
24387 			if (settings.url) {
24388 				html = '<iframe src="' + settings.url + '" tabindex="-1"></iframe>';
24389 			}
24390 
24391 			if (typeof(html) == "undefined") {
24392 				html = layout.renderHtml(self);
24393 			}
24394 
24395 			if (self.statusbar) {
24396 				footerHtml = self.statusbar.renderHtml();
24397 			}
24398 
24399 			return (
24400 				'<div id="' + id + '" class="' + self.classes() + '" hidefocus="1">' +
24401 					'<div class="' + self.classPrefix + 'reset" role="application">' +
24402 						headerHtml +
24403 						'<div id="' + id + '-body" class="' + self.classes('body') + '">' +
24404 							html +
24405 						'</div>' +
24406 						footerHtml +
24407 					'</div>' +
24408 				'</div>'
24409 			);
24410 		},
24411 
24412 		/**
24413 		 * Switches the window fullscreen mode.
24414 		 *
24415 		 * @method fullscreen
24416 		 * @param {Boolean} state True/false state.
24417 		 * @return {tinymce.ui.Window} Current window instance.
24418 		 */
24419 		fullscreen: function(state) {
24420 			var self = this, documentElement = document.documentElement, slowRendering, prefix = self.classPrefix, layoutRect;
24421 
24422 			if (state != self._fullscreen) {
24423 				DomUtils.on(window, 'resize', function() {
24424 					var time;
24425 
24426 					if (self._fullscreen) {
24427 						// Time the layout time if it's to slow use a timeout to not hog the CPU
24428 						if (!slowRendering) {
24429 							time = new Date().getTime();
24430 
24431 							var rect = DomUtils.getWindowSize();
24432 							self.moveTo(0, 0).resizeTo(rect.w, rect.h);
24433 
24434 							if ((new Date().getTime()) - time > 50) {
24435 								slowRendering = true;
24436 							}
24437 						} else {
24438 							if (!self._timer) {
24439 								self._timer = setTimeout(function() {
24440 									var rect = DomUtils.getWindowSize();
24441 									self.moveTo(0, 0).resizeTo(rect.w, rect.h);
24442 
24443 									self._timer = 0;
24444 								}, 50);
24445 							}
24446 						}
24447 					}
24448 				});
24449 
24450 				layoutRect = self.layoutRect();
24451 				self._fullscreen = state;
24452 
24453 				if (!state) {
24454 					self._borderBox = self.parseBox(self.settings.border);
24455 					self.getEl('head').style.display = '';
24456 					layoutRect.deltaH += layoutRect.headerH;
24457 					DomUtils.removeClass(documentElement, prefix + 'fullscreen');
24458 					DomUtils.removeClass(document.body, prefix + 'fullscreen');
24459 					self.removeClass('fullscreen');
24460 					self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h);
24461 				} else {
24462 					self._initial = {x: layoutRect.x, y: layoutRect.y, w: layoutRect.w, h: layoutRect.h};
24463 
24464 					self._borderBox = self.parseBox('0');
24465 					self.getEl('head').style.display = 'none';
24466 					layoutRect.deltaH -= layoutRect.headerH + 2;
24467 					DomUtils.addClass(documentElement, prefix + 'fullscreen');
24468 					DomUtils.addClass(document.body, prefix + 'fullscreen');
24469 					self.addClass('fullscreen');
24470 
24471 					var rect = DomUtils.getWindowSize();
24472 					self.moveTo(0, 0).resizeTo(rect.w, rect.h);
24473 				}
24474 			}
24475 
24476 			return self.reflow();
24477 		},
24478 
24479 		/**
24480 		 * Called after the control has been rendered.
24481 		 *
24482 		 * @method postRender
24483 		 */
24484 		postRender: function() {
24485 			var self = this, startPos;
24486 
24487 			setTimeout(function() {
24488 				self.addClass('in');
24489 			}, 0);
24490 
24491 			self._super();
24492 
24493 			if (self.statusbar) {
24494 				self.statusbar.postRender();
24495 			}
24496 
24497 			self.focus();
24498 
24499 			this.dragHelper = new DragHelper(self._id + '-dragh', {
24500 				start: function() {
24501 					startPos = {
24502 						x: self.layoutRect().x,
24503 						y: self.layoutRect().y
24504 					};
24505 				},
24506 
24507 				drag: function(e) {
24508 					self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY);
24509 				}
24510 			});
24511 
24512 			self.on('submit', function(e) {
24513 				if (!e.isDefaultPrevented()) {
24514 					self.close();
24515 				}
24516 			});
24517 		},
24518 
24519 		/**
24520 		 * Fires a submit event with the serialized form.
24521 		 *
24522 		 * @method submit
24523 		 * @return {Object} Event arguments object.
24524 		 */
24525 		submit: function() {
24526 			return this.fire('submit', {data: this.toJSON()});
24527 		},
24528 
24529 		/**
24530 		 * Removes the current control from DOM and from UI collections.
24531 		 *
24532 		 * @method remove
24533 		 * @return {tinymce.ui.Control} Current control instance.
24534 		 */
24535 		remove: function() {
24536 			var self = this, prefix = self.classPrefix;
24537 
24538 			self.dragHelper.destroy();
24539 			self._super();
24540 
24541 			if (self.statusbar) {
24542 				this.statusbar.remove();
24543 			}
24544 
24545 			if (self._fullscreen) {
24546 				DomUtils.removeClass(document.documentElement, prefix + 'fullscreen');
24547 				DomUtils.removeClass(document.body, prefix + 'fullscreen');
24548 			}
24549 		},
24550 
24551 		/**
24552 		 * Returns the contentWindow object of the iframe if it exists.
24553 		 *
24554 		 * @method getContentWindow
24555 		 * @return {Window} window object or null.
24556 		 */
24557 		getContentWindow: function() {
24558 			var ifr = this.getEl().getElementsByTagName('iframe')[0];
24559 			return ifr ? ifr.contentWindow : null;
24560 		}
24561 	});
24562 
24563 	return Window;
24564 });
24565 
24566 // Included from: js/tinymce/classes/ui/MessageBox.js
24567 
24568 /**
24569  * MessageBox.js
24570  *
24571  * Copyright, Moxiecode Systems AB
24572  * Released under LGPL License.
24573  *
24574  * License: http://www.tinymce.com/license
24575  * Contributing: http://www.tinymce.com/contributing
24576  */
24577 
24578 /**
24579  * This class is used to create MessageBoxes like alerts/confirms etc.
24580  *
24581  * @class tinymce.ui.Window
24582  * @extends tinymce.ui.FloatPanel
24583  */
24584 define("tinymce/ui/MessageBox", [
24585 	"tinymce/ui/Window"
24586 ], function(Window) {
24587 	"use strict";
24588 
24589 	var MessageBox = Window.extend({
24590 		/**
24591 		 * Constructs a instance with the specified settings.
24592 		 *
24593 		 * @constructor
24594 		 * @param {Object} settings Name/value object with settings.
24595 		 */
24596 		init: function(settings) {
24597 			settings = {
24598 				border: 1,
24599 				padding: 20,
24600 				layout: 'flex',
24601 				pack: "center",
24602 				align: "center",
24603 				containerCls: 'panel',
24604 				autoScroll: true,
24605 				buttons: {type: "button", text: "Ok", action: "ok"},
24606 				items: {
24607 					type: "label",
24608 					multiline: true,
24609 					maxWidth: 500,
24610 					maxHeight: 200
24611 				}
24612 			};
24613 
24614 			this._super(settings);
24615 		},
24616 
24617 		Statics: {
24618 			/**
24619 			 * Ok buttons constant.
24620 			 *
24621 			 * @static
24622 			 * @final
24623 			 * @field {Number} OK
24624 			 */
24625 			OK: 1,
24626 
24627 			/**
24628 			 * Ok/cancel buttons constant.
24629 			 *
24630 			 * @static
24631 			 * @final
24632 			 * @field {Number} OK_CANCEL
24633 			 */
24634 			OK_CANCEL: 2,
24635 
24636 			/**
24637 			 * yes/no buttons constant.
24638 			 *
24639 			 * @static
24640 			 * @final
24641 			 * @field {Number} YES_NO
24642 			 */
24643 			YES_NO: 3,
24644 
24645 			/**
24646 			 * yes/no/cancel buttons constant.
24647 			 *
24648 			 * @static
24649 			 * @final
24650 			 * @field {Number} YES_NO_CANCEL
24651 			 */
24652 			YES_NO_CANCEL: 4,
24653 
24654 			/**
24655 			 * Constructs a new message box and renders it to the body element.
24656 			 *
24657 			 * @static
24658 			 * @method msgBox
24659 			 * @param {Object} settings Name/value object with settings.
24660 			 */
24661 			msgBox: function(settings) {
24662 				var buttons, callback = settings.callback || function() {};
24663 
24664 				switch (settings.buttons) {
24665 					case MessageBox.OK_CANCEL:
24666 						buttons = [
24667 							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
24668 								e.control.parents()[1].close();
24669 								callback(true);
24670 							}},
24671 
24672 							{type: "button", text: "Cancel", onClick: function(e) {
24673 								e.control.parents()[1].close();
24674 								callback(false);
24675 							}}
24676 						];
24677 						break;
24678 
24679 					case MessageBox.YES_NO:
24680 						buttons = [
24681 							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
24682 								e.control.parents()[1].close();
24683 								callback(true);
24684 							}}
24685 						];
24686 						break;
24687 
24688 					case MessageBox.YES_NO_CANCEL:
24689 						buttons = [
24690 							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
24691 								e.control.parents()[1].close();
24692 							}}
24693 						];
24694 						break;
24695 
24696 					default:
24697 						buttons = [
24698 							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
24699 								e.control.parents()[1].close();
24700 								callback(true);
24701 							}}
24702 						];
24703 						break;
24704 				}
24705 
24706 				return new Window({
24707 					padding: 20,
24708 					x: settings.x,
24709 					y: settings.y,
24710 					minWidth: 300,
24711 					minHeight: 100,
24712 					layout: "flex",
24713 					pack: "center",
24714 					align: "center",
24715 					buttons: buttons,
24716 					title: settings.title,
24717 					role: 'alertdialog',
24718 					items: {
24719 						type: "label",
24720 						multiline: true,
24721 						maxWidth: 500,
24722 						maxHeight: 200,
24723 						text: settings.text
24724 					},
24725 					onPostRender: function() {
24726 						this.aria('describedby', this.items()[0]._id);
24727 					},
24728 					onClose: settings.onClose,
24729 					onCancel: function() {
24730 						callback(false);
24731 					}
24732 				}).renderTo(document.body).reflow();
24733 			},
24734 
24735 			/**
24736 			 * Creates a new alert dialog.
24737 			 *
24738 			 * @method alert
24739 			 * @param {Object} settings Settings for the alert dialog.
24740 			 * @param {function} [callback] Callback to execute when the user makes a choice.
24741 			 */
24742 			alert: function(settings, callback) {
24743 				if (typeof(settings) == "string") {
24744 					settings = {text: settings};
24745 				}
24746 
24747 				settings.callback = callback;
24748 				return MessageBox.msgBox(settings);
24749 			},
24750 
24751 			/**
24752 			 * Creates a new confirm dialog.
24753 			 *
24754 			 * @method confirm
24755 			 * @param {Object} settings Settings for the confirm dialog.
24756 			 * @param {function} [callback] Callback to execute when the user makes a choice.
24757 			 */
24758 			confirm: function(settings, callback) {
24759 				if (typeof(settings) == "string") {
24760 					settings = {text: settings};
24761 				}
24762 
24763 				settings.callback = callback;
24764 				settings.buttons = MessageBox.OK_CANCEL;
24765 
24766 				return MessageBox.msgBox(settings);
24767 			}
24768 		}
24769 	});
24770 
24771 	return MessageBox;
24772 });
24773 
24774 // Included from: js/tinymce/classes/WindowManager.js
24775 
24776 /**
24777  * WindowManager.js
24778  *
24779  * Copyright, Moxiecode Systems AB
24780  * Released under LGPL License.
24781  *
24782  * License: http://www.tinymce.com/license
24783  * Contributing: http://www.tinymce.com/contributing
24784  */
24785 
24786 /**
24787  * This class handles the creation of native windows and dialogs. This class can be extended to provide for example inline dialogs.
24788  *
24789  * @class tinymce.WindowManager
24790  * @example
24791  * // Opens a new dialog with the file.htm file and the size 320x240
24792  * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
24793  * tinymce.activeEditor.windowManager.open({
24794  *    url: 'file.htm',
24795  *    width: 320,
24796  *    height: 240
24797  * }, {
24798  *    custom_param: 1
24799  * });
24800  *
24801  * // Displays an alert box using the active editors window manager instance
24802  * tinymce.activeEditor.windowManager.alert('Hello world!');
24803  *
24804  * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
24805  * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
24806  *    if (s)
24807  *       tinymce.activeEditor.windowManager.alert("Ok");
24808  *    else
24809  *       tinymce.activeEditor.windowManager.alert("Cancel");
24810  * });
24811  */
24812 define("tinymce/WindowManager", [
24813 	"tinymce/ui/Window",
24814 	"tinymce/ui/MessageBox"
24815 ], function(Window, MessageBox) {
24816 	return function(editor) {
24817 		var self = this, windows = [];
24818 
24819 		function getTopMostWindow() {
24820 			if (windows.length) {
24821 				return windows[windows.length - 1];
24822 			}
24823 		}
24824 
24825 		self.windows = windows;
24826 
24827 		/**
24828 		 * Opens a new window.
24829 		 *
24830 		 * @method open
24831 		 * @param {Object} args Optional name/value settings collection contains things like width/height/url etc.
24832 		 * @option {String} title Window title.
24833 		 * @option {String} file URL of the file to open in the window.
24834 		 * @option {Number} width Width in pixels.
24835 		 * @option {Number} height Height in pixels.
24836 		 * @option {Boolean} resizable Specifies whether the popup window is resizable or not.
24837 		 * @option {Boolean} maximizable Specifies whether the popup window has a "maximize" button and can get maximized or not.
24838 		 * @option {String/Boolean} scrollbars Specifies whether the popup window can have scrollbars if required (i.e. content
24839 		 * larger than the popup size specified).
24840 		 */
24841 		self.open = function(args, params) {
24842 			var win;
24843 
24844 			editor.editorManager.activeEditor = editor;
24845 
24846 			args.title = args.title || ' ';
24847 
24848 			// Handle URL
24849 			args.url = args.url || args.file; // Legacy
24850 			if (args.url) {
24851 				args.width = parseInt(args.width || 320, 10);
24852 				args.height = parseInt(args.height || 240, 10);
24853 			}
24854 
24855 			// Handle body
24856 			if (args.body) {
24857 				args.items = {
24858 					defaults: args.defaults,
24859 					type: args.bodyType || 'form',
24860 					items: args.body
24861 				};
24862 			}
24863 
24864 			if (!args.url && !args.buttons) {
24865 				args.buttons = [
24866 					{text: 'Ok', subtype: 'primary', onclick: function() {
24867 						win.find('form')[0].submit();
24868 					}},
24869 
24870 					{text: 'Cancel', onclick: function() {
24871 						win.close();
24872 					}}
24873 				];
24874 			}
24875 
24876 			win = new Window(args);
24877 			windows.push(win);
24878 
24879 			win.on('close', function() {
24880 				var i = windows.length;
24881 
24882 				while (i--) {
24883 					if (windows[i] === win) {
24884 						windows.splice(i, 1);
24885 					}
24886 				}
24887 
24888 				editor.focus();
24889 			});
24890 
24891 			// Handle data
24892 			if (args.data) {
24893 				win.on('postRender', function() {
24894 					this.find('*').each(function(ctrl) {
24895 						var name = ctrl.name();
24896 
24897 						if (name in args.data) {
24898 							ctrl.value(args.data[name]);
24899 						}
24900 					});
24901 				});
24902 			}
24903 
24904 			// store args and parameters
24905 			win.features = args || {};
24906 			win.params = params || {};
24907 
24908 			// Takes a snapshot in the FocusManager of the selection before focus is lost to dialog
24909 			editor.nodeChanged();
24910 
24911 			return win.renderTo().reflow();
24912 		};
24913 
24914 		/**
24915 		 * Creates a alert dialog. Please don't use the blocking behavior of this
24916 		 * native version use the callback method instead then it can be extended.
24917 		 *
24918 		 * @method alert
24919 		 * @param {String} message Text to display in the new alert dialog.
24920 		 * @param {function} callback Callback function to be executed after the user has selected ok.
24921 		 * @param {Object} scope Optional scope to execute the callback in.
24922 		 * @example
24923 		 * // Displays an alert box using the active editors window manager instance
24924 		 * tinymce.activeEditor.windowManager.alert('Hello world!');
24925 		 */
24926 		self.alert = function(message, callback, scope) {
24927 			MessageBox.alert(message, function() {
24928 				if (callback) {
24929 					callback.call(scope || this);
24930 				} else {
24931 					editor.focus();
24932 				}
24933 			});
24934 		};
24935 
24936 		/**
24937 		 * Creates a confirm dialog. Please don't use the blocking behavior of this
24938 		 * native version use the callback method instead then it can be extended.
24939 		 *
24940 		 * @method confirm
24941 		 * @param {String} messageText to display in the new confirm dialog.
24942 		 * @param {function} callback Callback function to be executed after the user has selected ok or cancel.
24943 		 * @param {Object} scope Optional scope to execute the callback in.
24944 		 * @example
24945 		 * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm
24946 		 * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) {
24947 		 *    if (s)
24948 		 *       tinymce.activeEditor.windowManager.alert("Ok");
24949 		 *    else
24950 		 *       tinymce.activeEditor.windowManager.alert("Cancel");
24951 		 * });
24952 		 */
24953 		self.confirm = function(message, callback, scope) {
24954 			MessageBox.confirm(message, function(state) {
24955 				callback.call(scope || this, state);
24956 			});
24957 		};
24958 
24959 		/**
24960 		 * Closes the top most window.
24961 		 *
24962 		 * @method close
24963 		 */
24964 		self.close = function() {
24965 			if (getTopMostWindow()) {
24966 				getTopMostWindow().close();
24967 			}
24968 		};
24969 
24970 		/**
24971 		 * Returns the params of the last window open call. This can be used in iframe based
24972 		 * dialog to get params passed from the tinymce plugin.
24973 		 *
24974 		 * @example
24975 		 * var dialogArguments = top.tinymce.activeEditor.windowManager.getParams();
24976 		 *
24977 		 * @method getParams
24978 		 * @return {Object} Name/value object with parameters passed from windowManager.open call.
24979 		 */
24980 		self.getParams = function() {
24981 			return getTopMostWindow() ? getTopMostWindow().params : null;
24982 		};
24983 
24984 		/**
24985 		 * Sets the params of the last opened window.
24986 		 *
24987 		 * @method setParams
24988 		 * @param {Object} params Params object to set for the last opened window.
24989 		 */
24990 		self.setParams = function(params) {
24991 			if (getTopMostWindow()) {
24992 				getTopMostWindow().params = params;
24993 			}
24994 		};
24995 
24996 		/**
24997 		 * Returns the currently opened window objects.
24998 		 *
24999 		 * @method getWindows
25000 		 * @return {Array} Array of the currently opened windows.
25001 		 */
25002 		self.getWindows = function() {
25003 			return windows;
25004 		};
25005 	};
25006 });
25007 
25008 // Included from: js/tinymce/classes/util/Quirks.js
25009 
25010 /**
25011  * Quirks.js
25012  *
25013  * Copyright, Moxiecode Systems AB
25014  * Released under LGPL License.
25015  *
25016  * License: http://www.tinymce.com/license
25017  * Contributing: http://www.tinymce.com/contributing
25018  *
25019  * @ignore-file
25020  */
25021 
25022 /**
25023  * This file includes fixes for various browser quirks it's made to make it easy to add/remove browser specific fixes.
25024  *
25025  * @class tinymce.util.Quirks
25026  */
25027 define("tinymce/util/Quirks", [
25028 	"tinymce/util/VK",
25029 	"tinymce/dom/RangeUtils",
25030 	"tinymce/html/Node",
25031 	"tinymce/html/Entities",
25032 	"tinymce/Env",
25033 	"tinymce/util/Tools"
25034 ], function(VK, RangeUtils, Node, Entities, Env, Tools) {
25035 	return function(editor) {
25036 		var each = Tools.each;
25037 		var BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
25038 			settings = editor.settings, parser = editor.parser, serializer = editor.serializer;
25039 		var isGecko = Env.gecko, isIE = Env.ie, isWebKit = Env.webkit;
25040 
25041 		/**
25042 		 * Executes a command with a specific state this can be to enable/disable browser editing features.
25043 		 */
25044 		function setEditorCommandState(cmd, state) {
25045 			try {
25046 				editor.getDoc().execCommand(cmd, false, state);
25047 			} catch (ex) {
25048 				// Ignore
25049 			}
25050 		}
25051 
25052 		/**
25053 		 * Returns current IE document mode.
25054 		 */
25055 		function getDocumentMode() {
25056 			var documentMode = editor.getDoc().documentMode;
25057 
25058 			return documentMode ? documentMode : 6;
25059 		}
25060 
25061 		/**
25062 		 * Returns true/false if the event is prevented or not.
25063 		 *
25064 		 * @private
25065 		 * @param {Event} e Event object.
25066 		 * @return {Boolean} true/false if the event is prevented or not.
25067 		 */
25068 		function isDefaultPrevented(e) {
25069 			return e.isDefaultPrevented();
25070 		}
25071 
25072 		/**
25073 		 * Fixes a WebKit bug when deleting contents using backspace or delete key.
25074 		 * WebKit will produce a span element if you delete across two block elements.
25075 		 *
25076 		 * Example:
25077 		 * <h1>a</h1><p>|b</p>
25078 		 *
25079 		 * Will produce this on backspace:
25080 		 * <h1>a<span style="<all runtime styles>">b</span></p>
25081 		 *
25082 		 * This fixes the backspace to produce:
25083 		 * <h1>a|b</p>
25084 		 *
25085 		 * See bug: https://bugs.webkit.org/show_bug.cgi?id=45784
25086 		 *
25087 		 * This fixes the following delete scenarios:
25088 		 *  1. Delete by pressing backspace key.
25089 		 *  2. Delete by pressing delete key.
25090 		 *  3. Delete by pressing backspace key with ctrl/cmd (Word delete).
25091 		 *  4. Delete by pressing delete key with ctrl/cmd (Word delete).
25092 		 *  5. Delete by drag/dropping contents inside the editor.
25093 		 *  6. Delete by using Cut Ctrl+X/Cmd+X.
25094 		 *  7. Delete by selecting contents and writing a character.'
25095 		 *
25096 		 * This code is a ugly hack since writing full custom delete logic for just this bug
25097 		 * fix seemed like a huge task. I hope we can remove this before the year 2030. 
25098 		 */
25099 		function cleanupStylesWhenDeleting() {
25100 			var doc = editor.getDoc(), urlPrefix = 'data:text/mce-internal,';
25101 			var MutationObserver = window.MutationObserver, olderWebKit, dragStartRng;
25102 
25103 			// Add mini polyfill for older WebKits
25104 			// TODO: Remove this when old Safari versions gets updated
25105 			if (!MutationObserver) {
25106 				olderWebKit = true;
25107 
25108 				MutationObserver = function() {
25109 					var records = [], target;
25110 
25111 					function nodeInsert(e) {
25112 						var target = e.relatedNode || e.target;
25113 						records.push({target: target, addedNodes: [target]});
25114 					}
25115 
25116 					function attrModified(e) {
25117 						var target = e.relatedNode || e.target;
25118 						records.push({target: target, attributeName: e.attrName});
25119 					}
25120 
25121 					this.observe = function(node) {
25122 						target = node;
25123 						target.addEventListener('DOMSubtreeModified', nodeInsert, false);
25124 						target.addEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false);
25125 						target.addEventListener('DOMNodeInserted', nodeInsert, false);
25126 						target.addEventListener('DOMAttrModified', attrModified, false);
25127 					};
25128 
25129 					this.disconnect = function() {
25130 						target.removeEventListener('DOMSubtreeModified', nodeInsert, false);
25131 						target.removeEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false);
25132 						target.removeEventListener('DOMNodeInserted', nodeInsert, false);
25133 						target.removeEventListener('DOMAttrModified', attrModified, false);
25134 					};
25135 
25136 					this.takeRecords = function() {
25137 						return records;
25138 					};
25139 				};
25140 			}
25141 
25142 			function customDelete(isForward) {
25143 				var mutationObserver = new MutationObserver(function() {});
25144 
25145 				Tools.each(editor.getBody().getElementsByTagName('*'), function(elm) {
25146 					// Mark existing spans
25147 					if (elm.tagName == 'SPAN') {
25148 						elm.setAttribute('mce-data-marked', 1);
25149 					}
25150 
25151 					// Make sure all elements has a data-mce-style attribute
25152 					if (!elm.hasAttribute('data-mce-style') && elm.hasAttribute('style')) {
25153 						editor.dom.setAttrib(elm, 'style', elm.getAttribute('style'));
25154 					}
25155 				});
25156 
25157 				// Observe added nodes and style attribute changes
25158 				mutationObserver.observe(editor.getDoc(), {
25159 					childList: true,
25160 					attributes: true,
25161 					subtree: true,
25162 					attributeFilter: ['style']
25163 				});
25164 
25165 				editor.getDoc().execCommand(isForward ? 'ForwardDelete' : 'Delete', false, null);
25166 
25167 				var rng = editor.selection.getRng();
25168 				var caretElement = rng.startContainer.parentNode;
25169 
25170 				Tools.each(mutationObserver.takeRecords(), function(record) {
25171 					if (!dom.isChildOf(record.target, editor.getBody())) {
25172 						return;
25173 					}
25174 
25175 					// Restore style attribute to previous value
25176 					if (record.attributeName == "style") {
25177 						var oldValue = record.target.getAttribute('data-mce-style');
25178 
25179 						if (oldValue) {
25180 							record.target.setAttribute("style", oldValue);
25181 						} else {
25182 							record.target.removeAttribute("style");
25183 						}
25184 					}
25185 
25186 					// Remove all spans that isn't maked and retain selection
25187 					Tools.each(record.addedNodes, function(node) {
25188 						if (node.nodeName == "SPAN" && !node.getAttribute('mce-data-marked')) {
25189 							var offset, container;
25190 
25191 							if (node == caretElement) {
25192 								offset = rng.startOffset;
25193 								container = node.firstChild;
25194 							}
25195 
25196 							dom.remove(node, true);
25197 
25198 							if (container) {
25199 								rng.setStart(container, offset);
25200 								rng.setEnd(container, offset);
25201 								editor.selection.setRng(rng);
25202 							}
25203 						}
25204 					});
25205 				});
25206 
25207 				mutationObserver.disconnect();
25208 
25209 				// Remove any left over marks
25210 				Tools.each(editor.dom.select('span[mce-data-marked]'), function(span) {
25211 					span.removeAttribute('mce-data-marked');
25212 				});
25213 			}
25214 
25215 			editor.on('keydown', function(e) {
25216 				var isForward = e.keyCode == DELETE, isMeta = VK.metaKeyPressed(e);
25217 
25218 				if (!isDefaultPrevented(e) && (isForward || e.keyCode == BACKSPACE)) {
25219 					var rng = editor.selection.getRng(), container = rng.startContainer, offset = rng.startOffset;
25220 
25221 					// Ignore non meta delete in the where there is text before/after the caret
25222 					if (!isMeta && rng.collapsed && container.nodeType == 3) {
25223 						if (isForward ? offset < container.data.length : offset > 0) {
25224 							return;
25225 						}
25226 					}
25227 
25228 					e.preventDefault();
25229 
25230 					if (isMeta) {
25231 						editor.selection.getSel().modify("extend", isForward ? "forward" : "backward", "word");
25232 					}
25233 
25234 					customDelete(isForward);
25235 				}
25236 			});
25237 
25238 			editor.on('keypress', function(e) {
25239 				if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode && !VK.metaKeyPressed(e)) {
25240 					e.preventDefault();
25241 					customDelete(true);
25242 					editor.selection.setContent(String.fromCharCode(e.charCode));
25243 				}
25244 			});
25245 
25246 			editor.addCommand('Delete', function() {
25247 				customDelete();
25248 			});
25249 
25250 			editor.addCommand('ForwardDelete', function() {
25251 				customDelete(true);
25252 			});
25253 
25254 			// Older WebKits doesn't properly handle the clipboard so we can't add the rest
25255 			if (olderWebKit) {
25256 				return;
25257 			}
25258 
25259 			editor.on('dragstart', function(e) {
25260 				var selectionHtml;
25261 
25262 				if (editor.selection.isCollapsed() && e.target.tagName == 'IMG') {
25263 					selection.select(e.target);
25264 				}
25265 
25266 				dragStartRng = selection.getRng();
25267 				selectionHtml = editor.selection.getContent();
25268 
25269 				// Safari doesn't support custom dataTransfer items so we can only use URL and Text
25270 				if (selectionHtml.length > 0) {
25271 					e.dataTransfer.setData('URL', 'data:text/mce-internal,' + escape(selectionHtml));
25272 				}
25273 			});
25274 
25275 			editor.on('drop', function(e) {
25276 				if (!isDefaultPrevented(e)) {
25277 					var internalContent = e.dataTransfer.getData('URL');
25278 
25279 					if (!internalContent || internalContent.indexOf(urlPrefix) == -1 || !doc.caretRangeFromPoint) {
25280 						return;
25281 					}
25282 
25283 					internalContent = unescape(internalContent.substr(urlPrefix.length));
25284 					if (doc.caretRangeFromPoint) {
25285 						e.preventDefault();
25286 
25287 						// Safari has a weird issue where drag/dropping images sometimes
25288 						// produces a green plus icon. When this happens the caretRangeFromPoint
25289 						// will return "null" even though the x, y coordinate is correct.
25290 						// But if we detach the insert from the drop event we will get a proper range
25291 						window.setTimeout(function() {
25292 							var pointRng = doc.caretRangeFromPoint(e.x, e.y);
25293 
25294 							if (dragStartRng) {
25295 								selection.setRng(dragStartRng);
25296 								dragStartRng = null;
25297 							}
25298 
25299 							customDelete();
25300 
25301 							selection.setRng(pointRng);
25302 							editor.insertContent(internalContent);
25303 						}, 0);
25304 					}
25305 
25306 				}
25307 			});
25308 
25309 			editor.on('cut', function(e) {
25310 				if (!isDefaultPrevented(e) && e.clipboardData) {
25311 					e.preventDefault();
25312 					e.clipboardData.clearData();
25313 					e.clipboardData.setData('text/html', editor.selection.getContent());
25314 					e.clipboardData.setData('text/plain', editor.selection.getContent({format: 'text'}));
25315 					customDelete(true);
25316 				}
25317 			});
25318 		}
25319 
25320 		/**
25321 		 * Makes sure that the editor body becomes empty when backspace or delete is pressed in empty editors.
25322 		 *
25323 		 * For example:
25324 		 * <p><b>|</b></p>
25325 		 *
25326 		 * Or:
25327 		 * <h1>|</h1>
25328 		 *
25329 		 * Or:
25330 		 * [<h1></h1>]
25331 		 */
25332 		function emptyEditorWhenDeleting() {
25333 			function serializeRng(rng) {
25334 				var body = dom.create("body");
25335 				var contents = rng.cloneContents();
25336 				body.appendChild(contents);
25337 				return selection.serializer.serialize(body, {format: 'html'});
25338 			}
25339 
25340 			function allContentsSelected(rng) {
25341 				if (!rng.setStart) {
25342 					if (rng.item) {
25343 						return false;
25344 					}
25345 
25346 					var bodyRng = rng.duplicate();
25347 					bodyRng.moveToElementText(editor.getBody());
25348 					return RangeUtils.compareRanges(rng, bodyRng);
25349 				}
25350 
25351 				var selection = serializeRng(rng);
25352 
25353 				var allRng = dom.createRng();
25354 				allRng.selectNode(editor.getBody());
25355 
25356 				var allSelection = serializeRng(allRng);
25357 				return selection === allSelection;
25358 			}
25359 
25360 			editor.on('keydown', function(e) {
25361 				var keyCode = e.keyCode, isCollapsed, body;
25362 
25363 				// Empty the editor if it's needed for example backspace at <p><b>|</b></p>
25364 				if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
25365 					isCollapsed = editor.selection.isCollapsed();
25366 					body = editor.getBody();
25367 
25368 					// Selection is collapsed but the editor isn't empty
25369 					if (isCollapsed && !dom.isEmpty(body)) {
25370 						return;
25371 					}
25372 
25373 					// Selection isn't collapsed but not all the contents is selected
25374 					if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
25375 						return;
25376 					}
25377 
25378 					// Manually empty the editor
25379 					e.preventDefault();
25380 					editor.setContent('');
25381 
25382 					if (body.firstChild && dom.isBlock(body.firstChild)) {
25383 						editor.selection.setCursorLocation(body.firstChild, 0);
25384 					} else {
25385 						editor.selection.setCursorLocation(body, 0);
25386 					}
25387 
25388 					editor.nodeChanged();
25389 				}
25390 			});
25391 		}
25392 
25393 		/**
25394 		 * WebKit doesn't select all the nodes in the body when you press Ctrl+A.
25395 		 * IE selects more than the contents <body>[<p>a</p>]</body> instead of <body><p>[a]</p]</body> see bug #6438
25396 		 * This selects the whole body so that backspace/delete logic will delete everything
25397 		 */
25398 		function selectAll() {
25399 			editor.on('keydown', function(e) {
25400 				if (!isDefaultPrevented(e) && e.keyCode == 65 && VK.metaKeyPressed(e)) {
25401 					e.preventDefault();
25402 					editor.execCommand('SelectAll');
25403 				}
25404 			});
25405 		}
25406 
25407 		/**
25408 		 * WebKit has a weird issue where it some times fails to properly convert keypresses to input method keystrokes.
25409 		 * The IME on Mac doesn't initialize when it doesn't fire a proper focus event.
25410 		 *
25411 		 * This seems to happen when the user manages to click the documentElement element then the window doesn't get proper focus until
25412 		 * you enter a character into the editor.
25413 		 *
25414 		 * It also happens when the first focus in made to the body.
25415 		 *
25416 		 * See: https://bugs.webkit.org/show_bug.cgi?id=83566
25417 		 */
25418 		function inputMethodFocus() {
25419 			if (!editor.settings.content_editable) {
25420 				// Case 1 IME doesn't initialize if you focus the document
25421 				dom.bind(editor.getDoc(), 'focusin', function() {
25422 					selection.setRng(selection.getRng());
25423 				});
25424 
25425 				// Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event
25426 				dom.bind(editor.getDoc(), 'mousedown', function(e) {
25427 					if (e.target == editor.getDoc().documentElement) {
25428 						editor.getBody().focus();
25429 						selection.setRng(selection.getRng());
25430 					}
25431 				});
25432 			}
25433 		}
25434 
25435 		/**
25436 		 * Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the
25437 		 * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is
25438 		 * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js
25439 		 * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other
25440 		 * browsers.
25441 		 *
25442 		 * It also fixes a bug on Firefox where it's impossible to delete HR elements.
25443 		 */
25444 		function removeHrOnBackspace() {
25445 			editor.on('keydown', function(e) {
25446 				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
25447 					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
25448 						var node = selection.getNode();
25449 						var previousSibling = node.previousSibling;
25450 
25451 						if (node.nodeName == 'HR') {
25452 							dom.remove(node);
25453 							e.preventDefault();
25454 							return;
25455 						}
25456 
25457 						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
25458 							dom.remove(previousSibling);
25459 							e.preventDefault();
25460 						}
25461 					}
25462 				}
25463 			});
25464 		}
25465 
25466 		/**
25467 		 * Firefox 3.x has an issue where the body element won't get proper focus if you click out
25468 		 * side it's rectangle.
25469 		 */
25470 		function focusBody() {
25471 			// Fix for a focus bug in FF 3.x where the body element
25472 			// wouldn't get proper focus if the user clicked on the HTML element
25473 			if (!window.Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
25474 				editor.on('mousedown', function(e) {
25475 					if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {
25476 						var body = editor.getBody();
25477 
25478 						// Blur the body it's focused but not correctly focused
25479 						body.blur();
25480 
25481 						// Refocus the body after a little while
25482 						setTimeout(function() {
25483 							body.focus();
25484 						}, 0);
25485 					}
25486 				});
25487 			}
25488 		}
25489 
25490 		/**
25491 		 * WebKit has a bug where it isn't possible to select image, hr or anchor elements
25492 		 * by clicking on them so we need to fake that.
25493 		 */
25494 		function selectControlElements() {
25495 			editor.on('click', function(e) {
25496 				e = e.target;
25497 
25498 				// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
25499 				// WebKit can't even do simple things like selecting an image
25500 				// Needs tobe the setBaseAndExtend or it will fail to select floated images
25501 				if (/^(IMG|HR)$/.test(e.nodeName)) {
25502 					selection.getSel().setBaseAndExtent(e, 0, e, 1);
25503 				}
25504 
25505 				if (e.nodeName == 'A' && dom.hasClass(e, 'mce-item-anchor')) {
25506 					selection.select(e);
25507 				}
25508 
25509 				editor.nodeChanged();
25510 			});
25511 		}
25512 
25513 		/**
25514 		 * Fixes a Gecko bug where the style attribute gets added to the wrong element when deleting between two block elements.
25515 		 *
25516 		 * Fixes do backspace/delete on this:
25517 		 * <p>bla[ck</p><p style="color:red">r]ed</p>
25518 		 *
25519 		 * Would become:
25520 		 * <p>bla|ed</p>
25521 		 *
25522 		 * Instead of:
25523 		 * <p style="color:red">bla|ed</p>
25524 		 */
25525 		function removeStylesWhenDeletingAcrossBlockElements() {
25526 			function getAttributeApplyFunction() {
25527 				var template = dom.getAttribs(selection.getStart().cloneNode(false));
25528 
25529 				return function() {
25530 					var target = selection.getStart();
25531 
25532 					if (target !== editor.getBody()) {
25533 						dom.setAttrib(target, "style", null);
25534 
25535 						each(template, function(attr) {
25536 							target.setAttributeNode(attr.cloneNode(true));
25537 						});
25538 					}
25539 				};
25540 			}
25541 
25542 			function isSelectionAcrossElements() {
25543 				return !selection.isCollapsed() &&
25544 					dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock);
25545 			}
25546 
25547 			editor.on('keypress', function(e) {
25548 				var applyAttributes;
25549 
25550 				if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
25551 					applyAttributes = getAttributeApplyFunction();
25552 					editor.getDoc().execCommand('delete', false, null);
25553 					applyAttributes();
25554 					e.preventDefault();
25555 					return false;
25556 				}
25557 			});
25558 
25559 			dom.bind(editor.getDoc(), 'cut', function(e) {
25560 				var applyAttributes;
25561 
25562 				if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {
25563 					applyAttributes = getAttributeApplyFunction();
25564 
25565 					setTimeout(function() {
25566 						applyAttributes();
25567 					}, 0);
25568 				}
25569 			});
25570 		}
25571 
25572 		/**
25573 		 * Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5. It only fires the nodeChange
25574 		 * event every 50ms since it would other wise update the UI when you type and it hogs the CPU.
25575 		 */
25576 		function selectionChangeNodeChanged() {
25577 			var lastRng, selectionTimer;
25578 
25579 			editor.on('selectionchange', function() {
25580 				if (selectionTimer) {
25581 					clearTimeout(selectionTimer);
25582 					selectionTimer = 0;
25583 				}
25584 
25585 				selectionTimer = window.setTimeout(function() {
25586 					if (editor.removed) {
25587 						return;
25588 					}
25589 
25590 					var rng = selection.getRng();
25591 
25592 					// Compare the ranges to see if it was a real change or not
25593 					if (!lastRng || !RangeUtils.compareRanges(rng, lastRng)) {
25594 						editor.nodeChanged();
25595 						lastRng = rng;
25596 					}
25597 				}, 50);
25598 			});
25599 		}
25600 
25601 		/**
25602 		 * Screen readers on IE needs to have the role application set on the body.
25603 		 */
25604 		function ensureBodyHasRoleApplication() {
25605 			document.body.setAttribute("role", "application");
25606 		}
25607 
25608 		/**
25609 		 * Backspacing into a table behaves differently depending upon browser type.
25610 		 * Therefore, disable Backspace when cursor immediately follows a table.
25611 		 */
25612 		function disableBackspaceIntoATable() {
25613 			editor.on('keydown', function(e) {
25614 				if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
25615 					if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
25616 						var previousSibling = selection.getNode().previousSibling;
25617 						if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {
25618 							e.preventDefault();
25619 							return false;
25620 						}
25621 					}
25622 				}
25623 			});
25624 		}
25625 
25626 		/**
25627 		 * Old IE versions can't properly render BR elements in PRE tags white in contentEditable mode. So this
25628 		 * logic adds a \n before the BR so that it will get rendered.
25629 		 */
25630 		function addNewLinesBeforeBrInPre() {
25631 			// IE8+ rendering mode does the right thing with BR in PRE
25632 			if (getDocumentMode() > 7) {
25633 				return;
25634 			}
25635 
25636 			// Enable display: none in area and add a specific class that hides all BR elements in PRE to
25637 			// avoid the caret from getting stuck at the BR elements while pressing the right arrow key
25638 			setEditorCommandState('RespectVisibilityInDesign', true);
25639 			editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');
25640 			dom.addClass(editor.getBody(), 'mceHideBrInPre');
25641 
25642 			// Adds a \n before all BR elements in PRE to get them visual
25643 			parser.addNodeFilter('pre', function(nodes) {
25644 				var i = nodes.length, brNodes, j, brElm, sibling;
25645 
25646 				while (i--) {
25647 					brNodes = nodes[i].getAll('br');
25648 					j = brNodes.length;
25649 					while (j--) {
25650 						brElm = brNodes[j];
25651 
25652 						// Add \n before BR in PRE elements on older IE:s so the new lines get rendered
25653 						sibling = brElm.prev;
25654 						if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') {
25655 							sibling.value += '\n';
25656 						} else {
25657 							brElm.parent.insert(new Node('#text', 3), brElm, true).value = '\n';
25658 						}
25659 					}
25660 				}
25661 			});
25662 
25663 			// Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible
25664 			serializer.addNodeFilter('pre', function(nodes) {
25665 				var i = nodes.length, brNodes, j, brElm, sibling;
25666 
25667 				while (i--) {
25668 					brNodes = nodes[i].getAll('br');
25669 					j = brNodes.length;
25670 					while (j--) {
25671 						brElm = brNodes[j];
25672 						sibling = brElm.prev;
25673 						if (sibling && sibling.type == 3) {
25674 							sibling.value = sibling.value.replace(/\r?\n$/, '');
25675 						}
25676 					}
25677 				}
25678 			});
25679 		}
25680 
25681 		/**
25682 		 * Moves style width/height to attribute width/height when the user resizes an image on IE.
25683 		 */
25684 		function removePreSerializedStylesWhenSelectingControls() {
25685 			dom.bind(editor.getBody(), 'mouseup', function() {
25686 				var value, node = selection.getNode();
25687 
25688 				// Moved styles to attributes on IMG eements
25689 				if (node.nodeName == 'IMG') {
25690 					// Convert style width to width attribute
25691 					if ((value = dom.getStyle(node, 'width'))) {
25692 						dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, ''));
25693 						dom.setStyle(node, 'width', '');
25694 					}
25695 
25696 					// Convert style height to height attribute
25697 					if ((value = dom.getStyle(node, 'height'))) {
25698 						dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, ''));
25699 						dom.setStyle(node, 'height', '');
25700 					}
25701 				}
25702 			});
25703 		}
25704 
25705 		/**
25706 		 * Removes a blockquote when backspace is pressed at the beginning of it.
25707 		 *
25708 		 * For example:
25709 		 * <blockquote><p>|x</p></blockquote>
25710 		 *
25711 		 * Becomes:
25712 		 * <p>|x</p>
25713 		 */
25714 		function removeBlockQuoteOnBackSpace() {
25715 			// Add block quote deletion handler
25716 			editor.on('keydown', function(e) {
25717 				var rng, container, offset, root, parent;
25718 
25719 				if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) {
25720 					return;
25721 				}
25722 
25723 				rng = selection.getRng();
25724 				container = rng.startContainer;
25725 				offset = rng.startOffset;
25726 				root = dom.getRoot();
25727 				parent = container;
25728 
25729 				if (!rng.collapsed || offset !== 0) {
25730 					return;
25731 				}
25732 
25733 				while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) {
25734 					parent = parent.parentNode;
25735 				}
25736 
25737 				// Is the cursor at the beginning of a blockquote?
25738 				if (parent.tagName === 'BLOCKQUOTE') {
25739 					// Remove the blockquote
25740 					editor.formatter.toggle('blockquote', null, parent);
25741 
25742 					// Move the caret to the beginning of container
25743 					rng = dom.createRng();
25744 					rng.setStart(container, 0);
25745 					rng.setEnd(container, 0);
25746 					selection.setRng(rng);
25747 				}
25748 			});
25749 		}
25750 
25751 		/**
25752 		 * Sets various Gecko editing options on mouse down and before a execCommand to disable inline table editing that is broken etc.
25753 		 */
25754 		function setGeckoEditingOptions() {
25755 			function setOpts() {
25756 				editor._refreshContentEditable();
25757 
25758 				setEditorCommandState("StyleWithCSS", false);
25759 				setEditorCommandState("enableInlineTableEditing", false);
25760 
25761 				if (!settings.object_resizing) {
25762 					setEditorCommandState("enableObjectResizing", false);
25763 				}
25764 			}
25765 
25766 			if (!settings.readonly) {
25767 				editor.on('BeforeExecCommand MouseDown', setOpts);
25768 			}
25769 		}
25770 
25771 		/**
25772 		 * Fixes a gecko link bug, when a link is placed at the end of block elements there is
25773 		 * no way to move the caret behind the link. This fix adds a bogus br element after the link.
25774 		 *
25775 		 * For example this:
25776 		 * <p><b><a href="#">x</a></b></p>
25777 		 *
25778 		 * Becomes this:
25779 		 * <p><b><a href="#">x</a></b><br></p>
25780 		 */
25781 		function addBrAfterLastLinks() {
25782 			function fixLinks() {
25783 				each(dom.select('a'), function(node) {
25784 					var parentNode = node.parentNode, root = dom.getRoot();
25785 
25786 					if (parentNode.lastChild === node) {
25787 						while (parentNode && !dom.isBlock(parentNode)) {
25788 							if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) {
25789 								return;
25790 							}
25791 
25792 							parentNode = parentNode.parentNode;
25793 						}
25794 
25795 						dom.add(parentNode, 'br', {'data-mce-bogus': 1});
25796 					}
25797 				});
25798 			}
25799 
25800 			editor.on('SetContent ExecCommand', function(e) {
25801 				if (e.type == "setcontent" || e.command === 'mceInsertLink') {
25802 					fixLinks();
25803 				}
25804 			});
25805 		}
25806 
25807 		/**
25808 		 * WebKit will produce DIV elements here and there by default. But since TinyMCE uses paragraphs by
25809 		 * default we want to change that behavior.
25810 		 */
25811 		function setDefaultBlockType() {
25812 			if (settings.forced_root_block) {
25813 				editor.on('init', function() {
25814 					setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
25815 				});
25816 			}
25817 		}
25818 
25819 		/**
25820 		 * Removes ghost selections from images/tables on Gecko.
25821 		 */
25822 		function removeGhostSelection() {
25823 			editor.on('Undo Redo SetContent', function(e) {
25824 				if (!e.initial) {
25825 					editor.execCommand('mceRepaint');
25826 				}
25827 			});
25828 		}
25829 
25830 		/**
25831 		 * Deletes the selected image on IE instead of navigating to previous page.
25832 		 */
25833 		function deleteControlItemOnBackSpace() {
25834 			editor.on('keydown', function(e) {
25835 				var rng;
25836 
25837 				if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) {
25838 					rng = editor.getDoc().selection.createRange();
25839 					if (rng && rng.item) {
25840 						e.preventDefault();
25841 						editor.undoManager.beforeChange();
25842 						dom.remove(rng.item(0));
25843 						editor.undoManager.add();
25844 					}
25845 				}
25846 			});
25847 		}
25848 
25849 		/**
25850 		 * IE10 doesn't properly render block elements with the right height until you add contents to them.
25851 		 * This fixes that by adding a padding-right to all empty text block elements.
25852 		 * See: https://connect.microsoft.com/IE/feedback/details/743881
25853 		 */
25854 		function renderEmptyBlocksFix() {
25855 			var emptyBlocksCSS;
25856 
25857 			// IE10+
25858 			if (getDocumentMode() >= 10) {
25859 				emptyBlocksCSS = '';
25860 				each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {
25861 					emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';
25862 				});
25863 
25864 				editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');
25865 			}
25866 		}
25867 
25868 		/**
25869 		 * Old IE versions can't retain contents within noscript elements so this logic will store the contents
25870 		 * as a attribute and the insert that value as it's raw text when the DOM is serialized.
25871 		 */
25872 		function keepNoScriptContents() {
25873 			if (getDocumentMode() < 9) {
25874 				parser.addNodeFilter('noscript', function(nodes) {
25875 					var i = nodes.length, node, textNode;
25876 
25877 					while (i--) {
25878 						node = nodes[i];
25879 						textNode = node.firstChild;
25880 
25881 						if (textNode) {
25882 							node.attr('data-mce-innertext', textNode.value);
25883 						}
25884 					}
25885 				});
25886 
25887 				serializer.addNodeFilter('noscript', function(nodes) {
25888 					var i = nodes.length, node, textNode, value;
25889 
25890 					while (i--) {
25891 						node = nodes[i];
25892 						textNode = nodes[i].firstChild;
25893 
25894 						if (textNode) {
25895 							textNode.value = Entities.decode(textNode.value);
25896 						} else {
25897 							// Old IE can't retain noscript value so an attribute is used to store it
25898 							value = node.attributes.map['data-mce-innertext'];
25899 							if (value) {
25900 								node.attr('data-mce-innertext', null);
25901 								textNode = new Node('#text', 3);
25902 								textNode.value = value;
25903 								textNode.raw = true;
25904 								node.append(textNode);
25905 							}
25906 						}
25907 					}
25908 				});
25909 			}
25910 		}
25911 
25912 		/**
25913 		 * IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode.
25914 		 */
25915 		function fixCaretSelectionOfDocumentElementOnIe() {
25916 			var doc = dom.doc, body = doc.body, started, startRng, htmlElm;
25917 
25918 			// Return range from point or null if it failed
25919 			function rngFromPoint(x, y) {
25920 				var rng = body.createTextRange();
25921 
25922 				try {
25923 					rng.moveToPoint(x, y);
25924 				} catch (ex) {
25925 					// IE sometimes throws and exception, so lets just ignore it
25926 					rng = null;
25927 				}
25928 
25929 				return rng;
25930 			}
25931 
25932 			// Fires while the selection is changing
25933 			function selectionChange(e) {
25934 				var pointRng;
25935 
25936 				// Check if the button is down or not
25937 				if (e.button) {
25938 					// Create range from mouse position
25939 					pointRng = rngFromPoint(e.x, e.y);
25940 
25941 					if (pointRng) {
25942 						// Check if pointRange is before/after selection then change the endPoint
25943 						if (pointRng.compareEndPoints('StartToStart', startRng) > 0) {
25944 							pointRng.setEndPoint('StartToStart', startRng);
25945 						} else {
25946 							pointRng.setEndPoint('EndToEnd', startRng);
25947 						}
25948 
25949 						pointRng.select();
25950 					}
25951 				} else {
25952 					endSelection();
25953 				}
25954 			}
25955 
25956 			// Removes listeners
25957 			function endSelection() {
25958 				var rng = doc.selection.createRange();
25959 
25960 				// If the range is collapsed then use the last start range
25961 				if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) {
25962 					startRng.select();
25963 				}
25964 
25965 				dom.unbind(doc, 'mouseup', endSelection);
25966 				dom.unbind(doc, 'mousemove', selectionChange);
25967 				startRng = started = 0;
25968 			}
25969 
25970 			// Make HTML element unselectable since we are going to handle selection by hand
25971 			doc.documentElement.unselectable = true;
25972 
25973 			// Detect when user selects outside BODY
25974 			dom.bind(doc, 'mousedown contextmenu', function(e) {
25975 				if (e.target.nodeName === 'HTML') {
25976 					if (started) {
25977 						endSelection();
25978 					}
25979 
25980 					// Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML
25981 					htmlElm = doc.documentElement;
25982 					if (htmlElm.scrollHeight > htmlElm.clientHeight) {
25983 						return;
25984 					}
25985 
25986 					started = 1;
25987 					// Setup start position
25988 					startRng = rngFromPoint(e.x, e.y);
25989 					if (startRng) {
25990 						// Listen for selection change events
25991 						dom.bind(doc, 'mouseup', endSelection);
25992 						dom.bind(doc, 'mousemove', selectionChange);
25993 
25994 						dom.getRoot().focus();
25995 						startRng.select();
25996 					}
25997 				}
25998 			});
25999 		}
26000 
26001 		/**
26002 		 * Fixes selection issues where the caret can be placed between two inline elements like <b>a</b>|<b>b</b>
26003 		 * this fix will lean the caret right into the closest inline element.
26004 		 */
26005 		function normalizeSelection() {
26006 			// Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i> except for Ctrl+A since it selects everything
26007 			editor.on('keyup focusin mouseup', function(e) {
26008 				if (e.keyCode != 65 || !VK.metaKeyPressed(e)) {
26009 					selection.normalize();
26010 				}
26011 			}, true);
26012 		}
26013 
26014 		/**
26015 		 * Forces Gecko to render a broken image icon if it fails to load an image.
26016 		 */
26017 		function showBrokenImageIcon() {
26018 			editor.contentStyles.push(
26019 				'img:-moz-broken {' +
26020 					'-moz-force-broken-image-icon:1;' +
26021 					'min-width:24px;' +
26022 					'min-height:24px' +
26023 				'}'
26024 			);
26025 		}
26026 
26027 		/**
26028 		 * iOS has a bug where it's impossible to type if the document has a touchstart event
26029 		 * bound and the user touches the document while having the on screen keyboard visible.
26030 		 *
26031 		 * The touch event moves the focus to the parent document while having the caret inside the iframe
26032 		 * this fix moves the focus back into the iframe document.
26033 		 */
26034 		function restoreFocusOnKeyDown() {
26035 			if (!editor.inline) {
26036 				editor.on('keydown', function() {
26037 					if (document.activeElement == document.body) {
26038 						editor.getWin().focus();
26039 					}
26040 				});
26041 			}
26042 		}
26043 
26044 		/**
26045 		 * IE 11 has an annoying issue where you can't move focus into the editor
26046 		 * by clicking on the white area HTML element. We used to be able to to fix this with
26047 		 * the fixCaretSelectionOfDocumentElementOnIe fix. But since M$ removed the selection
26048 		 * object it's not possible anymore. So we need to hack in a ungly CSS to force the
26049 		 * body to be at least 150px. If the user clicks the HTML element out side this 150px region
26050 		 * we simply move the focus into the first paragraph. Not ideal since you loose the
26051 		 * positioning of the caret but goot enough for most cases.
26052 		 */
26053 		function bodyHeight() {
26054 			if (!editor.inline) {
26055 				editor.contentStyles.push('body {min-height: 150px}');
26056 				editor.on('click', function(e) {
26057 					if (e.target.nodeName == 'HTML') {
26058 						editor.getBody().focus();
26059 						editor.selection.normalize();
26060 						editor.nodeChanged();
26061 					}
26062 				});
26063 			}
26064 		}
26065 
26066 		/**
26067 		 * Firefox on Mac OS will move the browser back to the previous page if you press CMD+Left arrow.
26068 		 * You might then loose all your work so we need to block that behavior and replace it with our own.
26069 		 */
26070 		function blockCmdArrowNavigation() {
26071 			if (Env.mac) {
26072 				editor.on('keydown', function(e) {
26073 					if (VK.metaKeyPressed(e) && (e.keyCode == 37 || e.keyCode == 39)) {
26074 						e.preventDefault();
26075 						editor.selection.getSel().modify('move', e.keyCode == 37 ? 'backward' : 'forward', 'word');
26076 					}
26077 				});
26078 			}
26079 		}
26080 
26081 		/**
26082 		 * Disables the autolinking in IE 9+ this is then re-enabled by the autolink plugin.
26083 		 */
26084 		function disableAutoUrlDetect() {
26085 			setEditorCommandState("AutoUrlDetect", false);
26086 		}
26087 
26088 		/**
26089 		 * IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when
26090 		 * the iframe is hidden by display: none on a parent container. The DOM is actually out of sync
26091 		 * with innerHTML in this case. It's like IE adds shadow DOM BR elements that appears on innerHTML
26092 		 * but not as the lastChild of the body. However is we add a BR element to the body then remove it
26093 		 * it doesn't seem to add these BR elements makes sence right?!
26094 		 *
26095 		 * Example of what happens: <body>text</body> becomes <body>text<br><br></body>
26096 		 */
26097 		function doubleTrailingBrElements() {
26098 			if (!editor.inline) {
26099 				editor.on('focus blur beforegetcontent', function() {
26100 					var br = editor.dom.create('br');
26101 					editor.getBody().appendChild(br);
26102 					br.parentNode.removeChild(br);
26103 				}, true);
26104 			}
26105 		}
26106 
26107 		/**
26108 		 * iOS 7.1 introduced two new bugs:
26109 		 * 1) It's possible to open links within a contentEditable area by clicking on them.
26110 		 * 2) If you hold down the finger it will display the link/image touch callout menu.
26111 		 */
26112 		function tapLinksAndImages() {
26113 			editor.on('click', function(e) {
26114 				var elm = e.target;
26115 
26116 				do {
26117 					if (elm.tagName === 'A') {
26118 						e.preventDefault();
26119 						return;
26120 					}
26121 				} while ((elm = elm.parentNode));
26122 			});
26123 
26124 			editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}');
26125 		}
26126 
26127 		/**
26128 		 * WebKit has a bug where it will allow forms to be submitted if they are inside a contentEditable element.
26129 		 * For example this: <form><button></form>
26130 		 */
26131 		function blockFormSubmitInsideEditor() {
26132 			editor.on('init', function() {
26133 				editor.dom.bind(editor.getBody(), 'submit', function(e) {
26134 					e.preventDefault();
26135 				});
26136 			});
26137 		}
26138 
26139 		// All browsers
26140 		disableBackspaceIntoATable();
26141 		removeBlockQuoteOnBackSpace();
26142 		emptyEditorWhenDeleting();
26143 		normalizeSelection();
26144 
26145 		// WebKit
26146 		if (isWebKit) {
26147 			cleanupStylesWhenDeleting();
26148 			inputMethodFocus();
26149 			selectControlElements();
26150 			setDefaultBlockType();
26151 			blockFormSubmitInsideEditor();
26152 
26153 			// iOS
26154 			if (Env.iOS) {
26155 				selectionChangeNodeChanged();
26156 				restoreFocusOnKeyDown();
26157 				bodyHeight();
26158 				tapLinksAndImages();
26159 			} else {
26160 				selectAll();
26161 			}
26162 		}
26163 
26164 		// IE
26165 		if (isIE && Env.ie < 11) {
26166 			removeHrOnBackspace();
26167 			ensureBodyHasRoleApplication();
26168 			addNewLinesBeforeBrInPre();
26169 			removePreSerializedStylesWhenSelectingControls();
26170 			deleteControlItemOnBackSpace();
26171 			renderEmptyBlocksFix();
26172 			keepNoScriptContents();
26173 			fixCaretSelectionOfDocumentElementOnIe();
26174 		}
26175 
26176 		if (Env.ie >= 11) {
26177 			bodyHeight();
26178 			doubleTrailingBrElements();
26179 		}
26180 
26181 		if (Env.ie) {
26182 			selectAll();
26183 			disableAutoUrlDetect();
26184 		}
26185 
26186 		// Gecko
26187 		if (isGecko) {
26188 			removeHrOnBackspace();
26189 			focusBody();
26190 			removeStylesWhenDeletingAcrossBlockElements();
26191 			setGeckoEditingOptions();
26192 			addBrAfterLastLinks();
26193 			removeGhostSelection();
26194 			showBrokenImageIcon();
26195 			blockCmdArrowNavigation();
26196 		}
26197 	};
26198 });
26199 
26200 // Included from: js/tinymce/classes/util/Observable.js
26201 
26202 /**
26203  * Observable.js
26204  *
26205  * Copyright, Moxiecode Systems AB
26206  * Released under LGPL License.
26207  *
26208  * License: http://www.tinymce.com/license
26209  * Contributing: http://www.tinymce.com/contributing
26210  */
26211 
26212 /**
26213  * This mixin will add event binding logic to classes.
26214  *
26215  * @mixin tinymce.util.Observable
26216  */
26217 define("tinymce/util/Observable", [
26218 	"tinymce/util/EventDispatcher"
26219 ], function(EventDispatcher) {
26220 	function getEventDispatcher(obj) {
26221 		if (!obj._eventDispatcher) {
26222 			obj._eventDispatcher = new EventDispatcher({
26223 				scope: obj,
26224 				toggleEvent: function(name, state) {
26225 					if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) {
26226 						obj.toggleNativeEvent(name, state);
26227 					}
26228 				}
26229 			});
26230 		}
26231 
26232 		return obj._eventDispatcher;
26233 	}
26234 
26235 	return {
26236 		/**
26237 		 * Fires the specified event by name.
26238 		 *
26239 		 * @method fire
26240 		 * @param {String} name Name of the event to fire.
26241 		 * @param {Object?} args Event arguments.
26242 		 * @param {Boolean?} bubble True/false if the event is to be bubbled.
26243 		 * @return {Object} Event args instance passed in.
26244 		 * @example
26245 		 * instance.fire('event', {...});
26246 		 */
26247 		fire: function(name, args, bubble) {
26248 			var self = this;
26249 
26250 			// Prevent all events except the remove event after the instance has been removed
26251 			if (self.removed && name !== "remove") {
26252 				return args;
26253 			}
26254 
26255 			args = getEventDispatcher(self).fire(name, args, bubble);
26256 
26257 			// Bubble event up to parents
26258 			if (bubble !== false && self.parent) {
26259 				var parent = self.parent();
26260 				while (parent && !args.isPropagationStopped()) {
26261 					parent.fire(name, args, false);
26262 					parent = parent.parent();
26263 				}
26264 			}
26265 
26266 			return args;
26267 		},
26268 
26269 		/**
26270 		 * Binds an event listener to a specific event by name.
26271 		 *
26272 		 * @method on
26273 		 * @param {String} name Event name or space separated list of events to bind.
26274 		 * @param {callback} callback Callback to be executed when the event occurs.
26275 		 * @param {Boolean} first Optional flag if the event should be prepended. Use this with care.
26276 		 * @return {Object} Current class instance.
26277 		 * @example
26278 		 * instance.on('event', function(e) {
26279 		 *     // Callback logic
26280 		 * });
26281 		 */
26282 		on: function(name, callback, prepend) {
26283 			return getEventDispatcher(this).on(name, callback, prepend);
26284 		},
26285 
26286 		/**
26287 		 * Unbinds an event listener to a specific event by name.
26288 		 *
26289 		 * @method off
26290 		 * @param {String?} name Name of the event to unbind.
26291 		 * @param {callback?} callback Callback to unbind.
26292 		 * @return {Object} Current class instance.
26293 		 * @example
26294 		 * // Unbind specific callback
26295 		 * instance.off('event', handler);
26296 		 *
26297 		 * // Unbind all listeners by name
26298 		 * instance.off('event');
26299 		 *
26300 		 * // Unbind all events
26301 		 * instance.off();
26302 		 */
26303 		off: function(name, callback) {
26304 			return getEventDispatcher(this).off(name, callback);
26305 		},
26306 
26307 		/**
26308 		 * Returns true/false if the object has a event of the specified name.
26309 		 *
26310 		 * @method hasEventListeners
26311 		 * @param {String} name Name of the event to check for.
26312 		 * @return {Boolean} true/false if the event exists or not.
26313 		 */
26314 		hasEventListeners: function(name) {
26315 			return getEventDispatcher(this).has(name);
26316 		}
26317 	};
26318 });
26319 
26320 // Included from: js/tinymce/classes/EditorObservable.js
26321 
26322 /**
26323  * EditorObservable.js
26324  *
26325  * Copyright, Moxiecode Systems AB
26326  * Released under LGPL License.
26327  *
26328  * License: http://www.tinymce.com/license
26329  * Contributing: http://www.tinymce.com/contributing
26330  */
26331 
26332 /**
26333  * This mixin contains the event logic for the tinymce.Editor class.
26334  *
26335  * @mixin tinymce.EditorObservable
26336  * @extends tinymce.util.Observable
26337  */
26338 define("tinymce/EditorObservable", [
26339 	"tinymce/util/Observable",
26340 	"tinymce/dom/DOMUtils",
26341 	"tinymce/util/Tools"
26342 ], function(Observable, DOMUtils, Tools) {
26343 	var DOM = DOMUtils.DOM;
26344 
26345 	function getEventTarget(editor, eventName) {
26346 		if (eventName == 'selectionchange') {
26347 			return editor.getDoc();
26348 		}
26349 
26350 		// Need to bind mousedown/mouseup etc to document not body in iframe mode
26351 		// Since the user might click on the HTML element not the BODY
26352 		if (!editor.inline && /^mouse|click|contextmenu|drop/.test(eventName)) {
26353 			return editor.getDoc();
26354 		}
26355 
26356 		return editor.getBody();
26357 	}
26358 
26359 	function bindEventDelegate(editor, name) {
26360 		var eventRootSelector = editor.settings.event_root, editorManager = editor.editorManager;
26361 		var eventRootElm = editorManager.eventRootElm || getEventTarget(editor, name);
26362 
26363 		if (eventRootSelector) {
26364 			if (!editorManager.rootEvents) {
26365 				editorManager.rootEvents = {};
26366 
26367 				editorManager.on('RemoveEditor', function() {
26368 					if (!editorManager.activeEditor) {
26369 						DOM.unbind(eventRootElm);
26370 						delete editorManager.rootEvents;
26371 					}
26372 				});
26373 			}
26374 
26375 			if (editorManager.rootEvents[name]) {
26376 				return;
26377 			}
26378 
26379 			if (eventRootElm == editor.getBody()) {
26380 				eventRootElm = DOM.select(eventRootSelector)[0];
26381 				editorManager.eventRootElm = eventRootElm;
26382 			}
26383 
26384 			editorManager.rootEvents[name] = true;
26385 
26386 			DOM.bind(eventRootElm, name, function(e) {
26387 				var target = e.target, editors = editorManager.editors, i = editors.length;
26388 
26389 				while (i--) {
26390 					var body = editors[i].getBody();
26391 
26392 					if (body === target || DOM.isChildOf(target, body)) {
26393 						if (!editors[i].hidden) {
26394 							editors[i].fire(name, e);
26395 						}
26396 					}
26397 				}
26398 			});
26399 		} else {
26400 			editor.dom.bind(eventRootElm, name, function(e) {
26401 				if (!editor.hidden) {
26402 					editor.fire(name, e);
26403 				}
26404 			});
26405 		}
26406 	}
26407 
26408 	var EditorObservable = {
26409 		bindPendingEventDelegates: function() {
26410 			var self = this;
26411 
26412 			Tools.each(self._pendingNativeEvents, function(name) {
26413 				bindEventDelegate(self, name);
26414 			});
26415 		},
26416 
26417 		toggleNativeEvent: function(name, state) {
26418 			var self = this;
26419 
26420 			if (self.settings.readonly) {
26421 				return;
26422 			}
26423 
26424 			// Never bind focus/blur since the FocusManager fakes those
26425 			if (name == "focus" || name == "blur") {
26426 				return;
26427 			}
26428 
26429 			if (state) {
26430 				if (self.initialized) {
26431 					bindEventDelegate(self, name);
26432 				} else {
26433 					if (!self._pendingNativeEvents) {
26434 						self._pendingNativeEvents = [name];
26435 					} else {
26436 						self._pendingNativeEvents.push(name);
26437 					}
26438 				}
26439 			} else if (self.initialized) {
26440 				self.dom.unbind(getEventTarget(self, name), name);
26441 			}
26442 		}
26443 	};
26444 
26445 	EditorObservable = Tools.extend({}, Observable, EditorObservable);
26446 
26447 	return EditorObservable;
26448 });
26449 
26450 // Included from: js/tinymce/classes/Shortcuts.js
26451 
26452 /**
26453  * Shortcuts.js
26454  *
26455  * Copyright, Moxiecode Systems AB
26456  * Released under LGPL License.
26457  *
26458  * License: http://www.tinymce.com/license
26459  * Contributing: http://www.tinymce.com/contributing
26460  */
26461 
26462 /**
26463  * Contains all logic for handling of keyboard shortcuts.
26464  */
26465 define("tinymce/Shortcuts", [
26466 	"tinymce/util/Tools",
26467 	"tinymce/Env"
26468 ], function(Tools, Env) {
26469 	var each = Tools.each, explode = Tools.explode;
26470 
26471 	var keyCodeLookup = {
26472 		"f9": 120,
26473 		"f10": 121,
26474 		"f11": 122
26475 	};
26476 
26477 	return function(editor) {
26478 		var self = this, shortcuts = {};
26479 
26480 		editor.on('keyup keypress keydown', function(e) {
26481 			if (e.altKey || e.ctrlKey || e.metaKey) {
26482 				each(shortcuts, function(shortcut) {
26483 					var ctrlKey = Env.mac ? e.metaKey : e.ctrlKey;
26484 
26485 					if (shortcut.ctrl != ctrlKey || shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) {
26486 						return;
26487 					}
26488 
26489 					if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {
26490 						e.preventDefault();
26491 
26492 						if (e.type == "keydown") {
26493 							shortcut.func.call(shortcut.scope);
26494 						}
26495 
26496 						return true;
26497 					}
26498 				});
26499 			}
26500 		});
26501 
26502 		/**
26503 		 * Adds a keyboard shortcut for some command or function.
26504 		 *
26505 		 * @method addShortcut
26506 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
26507 		 * @param {String} desc Text description for the command.
26508 		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
26509 		 * @param {Object} sc Optional scope to execute the function in.
26510 		 * @return {Boolean} true/false state if the shortcut was added or not.
26511 		 */
26512 		self.add = function(pattern, desc, cmdFunc, scope) {
26513 			var cmd;
26514 
26515 			cmd = cmdFunc;
26516 
26517 			if (typeof(cmdFunc) === 'string') {
26518 				cmdFunc = function() {
26519 					editor.execCommand(cmd, false, null);
26520 				};
26521 			} else if (Tools.isArray(cmd)) {
26522 				cmdFunc = function() {
26523 					editor.execCommand(cmd[0], cmd[1], cmd[2]);
26524 				};
26525 			}
26526 
26527 			each(explode(pattern.toLowerCase()), function(pattern) {
26528 				var shortcut = {
26529 					func: cmdFunc,
26530 					scope: scope || editor,
26531 					desc: editor.translate(desc),
26532 					alt: false,
26533 					ctrl: false,
26534 					shift: false
26535 				};
26536 
26537 				each(explode(pattern, '+'), function(value) {
26538 					switch (value) {
26539 						case 'alt':
26540 						case 'ctrl':
26541 						case 'shift':
26542 							shortcut[value] = true;
26543 							break;
26544 
26545 						default:
26546 							shortcut.charCode = value.charCodeAt(0);
26547 							shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
26548 					}
26549 				});
26550 
26551 				shortcuts[
26552 					(shortcut.ctrl ? 'ctrl' : '') + ',' +
26553 					(shortcut.alt ? 'alt' : '') + ',' +
26554 					(shortcut.shift ? 'shift' : '') + ',' +
26555 					shortcut.keyCode
26556 				] = shortcut;
26557 			});
26558 
26559 			return true;
26560 		};
26561 	};
26562 });
26563 
26564 // Included from: js/tinymce/classes/Editor.js
26565 
26566 /**
26567  * Editor.js
26568  *
26569  * Copyright, Moxiecode Systems AB
26570  * Released under LGPL License.
26571  *
26572  * License: http://www.tinymce.com/license
26573  * Contributing: http://www.tinymce.com/contributing
26574  */
26575 
26576 /*jshint scripturl:true */
26577 
26578 /**
26579  * Include the base event class documentation.
26580  *
26581  * @include ../../../tools/docs/tinymce.Event.js
26582  */
26583 
26584 /**
26585  * This class contains the core logic for a TinyMCE editor.
26586  *
26587  * @class tinymce.Editor
26588  * @mixes tinymce.util.Observable
26589  * @example
26590  * // Add a class to all paragraphs in the editor.
26591  * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
26592  *
26593  * // Gets the current editors selection as text
26594  * tinymce.activeEditor.selection.getContent({format: 'text'});
26595  *
26596  * // Creates a new editor instance
26597  * var ed = new tinymce.Editor('textareaid', {
26598  *     some_setting: 1
26599  * }, tinymce.EditorManager);
26600  *
26601  * // Select each item the user clicks on
26602  * ed.on('click', function(e) {
26603  *     ed.selection.select(e.target);
26604  * });
26605  *
26606  * ed.render();
26607  */
26608 define("tinymce/Editor", [
26609 	"tinymce/dom/DOMUtils",
26610 	"tinymce/AddOnManager",
26611 	"tinymce/html/Node",
26612 	"tinymce/dom/Serializer",
26613 	"tinymce/html/Serializer",
26614 	"tinymce/dom/Selection",
26615 	"tinymce/Formatter",
26616 	"tinymce/UndoManager",
26617 	"tinymce/EnterKey",
26618 	"tinymce/ForceBlocks",
26619 	"tinymce/EditorCommands",
26620 	"tinymce/util/URI",
26621 	"tinymce/dom/ScriptLoader",
26622 	"tinymce/dom/EventUtils",
26623 	"tinymce/WindowManager",
26624 	"tinymce/html/Schema",
26625 	"tinymce/html/DomParser",
26626 	"tinymce/util/Quirks",
26627 	"tinymce/Env",
26628 	"tinymce/util/Tools",
26629 	"tinymce/EditorObservable",
26630 	"tinymce/Shortcuts"
26631 ], function(
26632 	DOMUtils, AddOnManager, Node, DomSerializer, Serializer,
26633 	Selection, Formatter, UndoManager, EnterKey, ForceBlocks, EditorCommands,
26634 	URI, ScriptLoader, EventUtils, WindowManager,
26635 	Schema, DomParser, Quirks, Env, Tools, EditorObservable, Shortcuts
26636 ) {
26637 	// Shorten these names
26638 	var DOM = DOMUtils.DOM, ThemeManager = AddOnManager.ThemeManager, PluginManager = AddOnManager.PluginManager;
26639 	var extend = Tools.extend, each = Tools.each, explode = Tools.explode;
26640 	var inArray = Tools.inArray, trim = Tools.trim, resolve = Tools.resolve;
26641 	var Event = EventUtils.Event;
26642 	var isGecko = Env.gecko, ie = Env.ie;
26643 
26644 	/**
26645 	 * Include documentation for all the events.
26646 	 *
26647 	 * @include ../../../tools/docs/tinymce.Editor.js
26648 	 */
26649 
26650 	/**
26651 	 * Constructs a editor instance by id.
26652 	 *
26653 	 * @constructor
26654 	 * @method Editor
26655 	 * @param {String} id Unique id for the editor.
26656 	 * @param {Object} settings Settings for the editor.
26657 	 * @param {tinymce.EditorManager} editorManager EditorManager instance.
26658 	 * @author Moxiecode
26659 	 */
26660 	function Editor(id, settings, editorManager) {
26661 		var self = this, documentBaseUrl, baseUri;
26662 
26663 		documentBaseUrl = self.documentBaseUrl = editorManager.documentBaseURL;
26664 		baseUri = editorManager.baseURI;
26665 
26666 		/**
26667 		 * Name/value collection with editor settings.
26668 		 *
26669 		 * @property settings
26670 		 * @type Object
26671 		 * @example
26672 		 * // Get the value of the theme setting
26673 		 * tinymce.activeEditor.windowManager.alert("You are using the " + tinymce.activeEditor.settings.theme + " theme");
26674 		 */
26675 		self.settings = settings = extend({
26676 			id: id,
26677 			theme: 'modern',
26678 			delta_width: 0,
26679 			delta_height: 0,
26680 			popup_css: '',
26681 			plugins: '',
26682 			document_base_url: documentBaseUrl,
26683 			add_form_submit_trigger: true,
26684 			submit_patch: true,
26685 			add_unload_trigger: true,
26686 			convert_urls: true,
26687 			relative_urls: true,
26688 			remove_script_host: true,
26689 			object_resizing: true,
26690 			doctype: '<!DOCTYPE html>',
26691 			visual: true,
26692 			font_size_style_values: 'xx-small,x-small,small,medium,large,x-large,xx-large',
26693 
26694 			// See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
26695 			font_size_legacy_values: 'xx-small,small,medium,large,x-large,xx-large,300%',
26696 			forced_root_block: 'p',
26697 			hidden_input: true,
26698 			padd_empty_editor: true,
26699 			render_ui: true,
26700 			indentation: '30px',
26701 			inline_styles: true,
26702 			convert_fonts_to_spans: true,
26703 			indent: 'simple',
26704 			indent_before: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,' +
26705 				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
26706 			indent_after: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,' +
26707 				'tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
26708 			validate: true,
26709 			entity_encoding: 'named',
26710 			url_converter: self.convertURL,
26711 			url_converter_scope: self,
26712 			ie7_compat: true
26713 		}, settings);
26714 
26715 		AddOnManager.language = settings.language || 'en';
26716 		AddOnManager.languageLoad = settings.language_load;
26717 
26718 		AddOnManager.baseURL = editorManager.baseURL;
26719 
26720 		/**
26721 		 * Editor instance id, normally the same as the div/textarea that was replaced.
26722 		 *
26723 		 * @property id
26724 		 * @type String
26725 		 */
26726 		self.id = settings.id = id;
26727 
26728 		/**
26729 		 * State to force the editor to return false on a isDirty call.
26730 		 *
26731 		 * @property isNotDirty
26732 		 * @type Boolean
26733 		 * @example
26734 		 * function ajaxSave() {
26735 		 *     var ed = tinymce.get('elm1');
26736 		 *
26737 		 *     // Save contents using some XHR call
26738 		 *     alert(ed.getContent());
26739 		 *
26740 		 *     ed.isNotDirty = true; // Force not dirty state
26741 		 * }
26742 		 */
26743 		self.isNotDirty = true;
26744 
26745 		/**
26746 		 * Name/Value object containting plugin instances.
26747 		 *
26748 		 * @property plugins
26749 		 * @type Object
26750 		 * @example
26751 		 * // Execute a method inside a plugin directly
26752 		 * tinymce.activeEditor.plugins.someplugin.someMethod();
26753 		 */
26754 		self.plugins = {};
26755 
26756 		/**
26757 		 * URI object to document configured for the TinyMCE instance.
26758 		 *
26759 		 * @property documentBaseURI
26760 		 * @type tinymce.util.URI
26761 		 * @example
26762 		 * // Get relative URL from the location of document_base_url
26763 		 * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm');
26764 		 *
26765 		 * // Get absolute URL from the location of document_base_url
26766 		 * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm');
26767 		 */
26768 		self.documentBaseURI = new URI(settings.document_base_url || documentBaseUrl, {
26769 			base_uri: baseUri
26770 		});
26771 
26772 		/**
26773 		 * URI object to current document that holds the TinyMCE editor instance.
26774 		 *
26775 		 * @property baseURI
26776 		 * @type tinymce.util.URI
26777 		 * @example
26778 		 * // Get relative URL from the location of the API
26779 		 * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm');
26780 		 *
26781 		 * // Get absolute URL from the location of the API
26782 		 * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm');
26783 		 */
26784 		self.baseURI = baseUri;
26785 
26786 		/**
26787 		 * Array with CSS files to load into the iframe.
26788 		 *
26789 		 * @property contentCSS
26790 		 * @type Array
26791 		 */
26792 		self.contentCSS = [];
26793 
26794 		/**
26795 		 * Array of CSS styles to add to head of document when the editor loads.
26796 		 *
26797 		 * @property contentStyles
26798 		 * @type Array
26799 		 */
26800 		self.contentStyles = [];
26801 
26802 		// Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
26803 		self.shortcuts = new Shortcuts(self);
26804 
26805 		// Internal command handler objects
26806 		self.execCommands = {};
26807 		self.queryStateCommands = {};
26808 		self.queryValueCommands = {};
26809 		self.loadedCSS = {};
26810 
26811 		self.suffix = editorManager.suffix;
26812 		self.editorManager = editorManager;
26813 		self.inline = settings.inline;
26814 
26815 		// Call setup
26816 		editorManager.fire('SetupEditor', self);
26817 		self.execCallback('setup', self);
26818 	}
26819 
26820 	Editor.prototype = {
26821 		/**
26822 		 * Renderes the editor/adds it to the page.
26823 		 *
26824 		 * @method render
26825 		 */
26826 		render: function() {
26827 			var self = this, settings = self.settings, id = self.id, suffix = self.suffix;
26828 
26829 			function readyHandler() {
26830 				DOM.unbind(window, 'ready', readyHandler);
26831 				self.render();
26832 			}
26833 
26834 			// Page is not loaded yet, wait for it
26835 			if (!Event.domLoaded) {
26836 				DOM.bind(window, 'ready', readyHandler);
26837 				return;
26838 			}
26839 
26840 			// Element not found, then skip initialization
26841 			if (!self.getElement()) {
26842 				return;
26843 			}
26844 
26845 			// No editable support old iOS versions etc
26846 			if (!Env.contentEditable) {
26847 				return;
26848 			}
26849 
26850 			// Hide target element early to prevent content flashing
26851 			if (!settings.inline) {
26852 				self.orgVisibility = self.getElement().style.visibility;
26853 				self.getElement().style.visibility = 'hidden';
26854 			} else {
26855 				self.inline = true;
26856 			}
26857 
26858 			var form = self.getElement().form || DOM.getParent(id, 'form');
26859 			if (form) {
26860 				self.formElement = form;
26861 
26862 				// Add hidden input for non input elements inside form elements
26863 				if (settings.hidden_input && !/TEXTAREA|INPUT/i.test(self.getElement().nodeName)) {
26864 					DOM.insertAfter(DOM.create('input', {type: 'hidden', name: id}), id);
26865 					self.hasHiddenInput = true;
26866 				}
26867 
26868 				// Pass submit/reset from form to editor instance
26869 				self.formEventDelegate = function(e) {
26870 					self.fire(e.type, e);
26871 				};
26872 
26873 				DOM.bind(form, 'submit reset', self.formEventDelegate);
26874 
26875 				// Reset contents in editor when the form is reset
26876 				self.on('reset', function() {
26877 					self.setContent(self.startContent, {format: 'raw'});
26878 				});
26879 
26880 				// Check page uses id="submit" or name="submit" for it's submit button
26881 				if (settings.submit_patch && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) {
26882 					form._mceOldSubmit = form.submit;
26883 					form.submit = function() {
26884 						self.editorManager.triggerSave();
26885 						self.isNotDirty = true;
26886 
26887 						return form._mceOldSubmit(form);
26888 					};
26889 				}
26890 			}
26891 
26892 			/**
26893 			 * Window manager reference, use this to open new windows and dialogs.
26894 			 *
26895 			 * @property windowManager
26896 			 * @type tinymce.WindowManager
26897 			 * @example
26898 			 * // Shows an alert message
26899 			 * tinymce.activeEditor.windowManager.alert('Hello world!');
26900 			 *
26901 			 * // Opens a new dialog with the file.htm file and the size 320x240
26902 			 * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog.
26903 			 * tinymce.activeEditor.windowManager.open({
26904 			 *    url: 'file.htm',
26905 			 *    width: 320,
26906 			 *    height: 240
26907 			 * }, {
26908 			 *    custom_param: 1
26909 			 * });
26910 			 */
26911 			self.windowManager = new WindowManager(self);
26912 
26913 			if (settings.encoding == 'xml') {
26914 				self.on('GetContent', function(e) {
26915 					if (e.save) {
26916 						e.content = DOM.encode(e.content);
26917 					}
26918 				});
26919 			}
26920 
26921 			if (settings.add_form_submit_trigger) {
26922 				self.on('submit', function() {
26923 					if (self.initialized) {
26924 						self.save();
26925 					}
26926 				});
26927 			}
26928 
26929 			if (settings.add_unload_trigger) {
26930 				self._beforeUnload = function() {
26931 					if (self.initialized && !self.destroyed && !self.isHidden()) {
26932 						self.save({format: 'raw', no_events: true, set_dirty: false});
26933 					}
26934 				};
26935 
26936 				self.editorManager.on('BeforeUnload', self._beforeUnload);
26937 			}
26938 
26939 			// Load scripts
26940 			function loadScripts() {
26941 				var scriptLoader = ScriptLoader.ScriptLoader;
26942 
26943 				if (settings.language && settings.language != 'en' && !settings.language_url) {
26944 					settings.language_url = self.editorManager.baseURL + '/langs/' + settings.language + '.js';
26945 				}
26946 
26947 				if (settings.language_url) {
26948 					scriptLoader.add(settings.language_url);
26949 				}
26950 
26951 				if (settings.theme && typeof settings.theme != "function" &&
26952 					settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) {
26953 					var themeUrl = settings.theme_url;
26954 
26955 					if (themeUrl) {
26956 						themeUrl = self.documentBaseURI.toAbsolute(themeUrl);
26957 					} else {
26958 						themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js';
26959 					}
26960 
26961 					ThemeManager.load(settings.theme, themeUrl);
26962 				}
26963 
26964 				if (Tools.isArray(settings.plugins)) {
26965 					settings.plugins = settings.plugins.join(' ');
26966 				}
26967 
26968 				each(settings.external_plugins, function(url, name) {
26969 					PluginManager.load(name, url);
26970 					settings.plugins += ' ' + name;
26971 				});
26972 
26973 				each(settings.plugins.split(/[ ,]/), function(plugin) {
26974 					plugin = trim(plugin);
26975 
26976 					if (plugin && !PluginManager.urls[plugin]) {
26977 						if (plugin.charAt(0) == '-') {
26978 							plugin = plugin.substr(1, plugin.length);
26979 
26980 							var dependencies = PluginManager.dependencies(plugin);
26981 
26982 							each(dependencies, function(dep) {
26983 								var defaultSettings = {
26984 									prefix:'plugins/',
26985 									resource: dep,
26986 									suffix:'/plugin' + suffix + '.js'
26987 								};
26988 
26989 								dep = PluginManager.createUrl(defaultSettings, dep);
26990 								PluginManager.load(dep.resource, dep);
26991 							});
26992 						} else {
26993 							PluginManager.load(plugin, {
26994 								prefix: 'plugins/',
26995 								resource: plugin,
26996 								suffix: '/plugin' + suffix + '.js'
26997 							});
26998 						}
26999 					}
27000 				});
27001 
27002 				scriptLoader.loadQueue(function() {
27003 					if (!self.removed) {
27004 						self.init();
27005 					}
27006 				});
27007 			}
27008 
27009 			loadScripts();
27010 		},
27011 
27012 		/**
27013 		 * Initializes the editor this will be called automatically when
27014 		 * all plugins/themes and language packs are loaded by the rendered method.
27015 		 * This method will setup the iframe and create the theme and plugin instances.
27016 		 *
27017 		 * @method init
27018 		 */
27019 		init: function() {
27020 			var self = this, settings = self.settings, elm = self.getElement();
27021 			var w, h, minHeight, n, o, Theme, url, bodyId, bodyClass, re, i, initializedPlugins = [];
27022 
27023 			self.rtl = this.editorManager.i18n.rtl;
27024 			self.editorManager.add(self);
27025 
27026 			settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', self.getLang('aria.rich_text_area'));
27027 
27028 			/**
27029 			 * Reference to the theme instance that was used to generate the UI.
27030 			 *
27031 			 * @property theme
27032 			 * @type tinymce.Theme
27033 			 * @example
27034 			 * // Executes a method on the theme directly
27035 			 * tinymce.activeEditor.theme.someMethod();
27036 			 */
27037 			if (settings.theme) {
27038 				if (typeof settings.theme != "function") {
27039 					settings.theme = settings.theme.replace(/-/, '');
27040 					Theme = ThemeManager.get(settings.theme);
27041 					self.theme = new Theme(self, ThemeManager.urls[settings.theme]);
27042 
27043 					if (self.theme.init) {
27044 						self.theme.init(self, ThemeManager.urls[settings.theme] || self.documentBaseUrl.replace(/\/$/, ''));
27045 					}
27046 				} else {
27047 					self.theme = settings.theme;
27048 				}
27049 			}
27050 
27051 			function initPlugin(plugin) {
27052 				var Plugin = PluginManager.get(plugin), pluginUrl, pluginInstance;
27053 
27054 				pluginUrl = PluginManager.urls[plugin] || self.documentBaseUrl.replace(/\/$/, '');
27055 				plugin = trim(plugin);
27056 				if (Plugin && inArray(initializedPlugins, plugin) === -1) {
27057 					each(PluginManager.dependencies(plugin), function(dep){
27058 						initPlugin(dep);
27059 					});
27060 
27061 					pluginInstance = new Plugin(self, pluginUrl);
27062 
27063 					self.plugins[plugin] = pluginInstance;
27064 
27065 					if (pluginInstance.init) {
27066 						pluginInstance.init(self, pluginUrl);
27067 						initializedPlugins.push(plugin);
27068 					}
27069 				}
27070 			}
27071 
27072 			// Create all plugins
27073 			each(settings.plugins.replace(/\-/g, '').split(/[ ,]/), initPlugin);
27074 
27075 			// Measure box
27076 			if (settings.render_ui && self.theme) {
27077 				self.orgDisplay = elm.style.display;
27078 
27079 				if (typeof settings.theme != "function") {
27080 					w = settings.width || elm.style.width || elm.offsetWidth;
27081 					h = settings.height || elm.style.height || elm.offsetHeight;
27082 					minHeight = settings.min_height || 100;
27083 					re = /^[0-9\.]+(|px)$/i;
27084 
27085 					if (re.test('' + w)) {
27086 						w = Math.max(parseInt(w, 10), 100);
27087 					}
27088 
27089 					if (re.test('' + h)) {
27090 						h = Math.max(parseInt(h, 10), minHeight);
27091 					}
27092 
27093 					// Render UI
27094 					o = self.theme.renderUI({
27095 						targetNode: elm,
27096 						width: w,
27097 						height: h,
27098 						deltaWidth: settings.delta_width,
27099 						deltaHeight: settings.delta_height
27100 					});
27101 
27102 					// Resize editor
27103 					if (!settings.content_editable) {
27104 						DOM.setStyles(o.sizeContainer || o.editorContainer, {
27105 							wi2dth: w,
27106 							// TODO: Fix this
27107 							h2eight: h
27108 						});
27109 
27110 						h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
27111 						if (h < minHeight) {
27112 							h = minHeight;
27113 						}
27114 					}
27115 				} else {
27116 					o = settings.theme(self, elm);
27117 
27118 					// Convert element type to id:s
27119 					if (o.editorContainer.nodeType) {
27120 						o.editorContainer = o.editorContainer.id = o.editorContainer.id || self.id + "_parent";
27121 					}
27122 
27123 					// Convert element type to id:s
27124 					if (o.iframeContainer.nodeType) {
27125 						o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || self.id + "_iframecontainer";
27126 					}
27127 
27128 					// Use specified iframe height or the targets offsetHeight
27129 					h = o.iframeHeight || elm.offsetHeight;
27130 				}
27131 
27132 				self.editorContainer = o.editorContainer;
27133 			}
27134 
27135 			// Load specified content CSS last
27136 			if (settings.content_css) {
27137 				each(explode(settings.content_css), function(u) {
27138 					self.contentCSS.push(self.documentBaseURI.toAbsolute(u));
27139 				});
27140 			}
27141 
27142 			// Load specified content CSS last
27143 			if (settings.content_style) {
27144 				self.contentStyles.push(settings.content_style);
27145 			}
27146 
27147 			// Content editable mode ends here
27148 			if (settings.content_editable) {
27149 				elm = n = o = null; // Fix IE leak
27150 				return self.initContentBody();
27151 			}
27152 
27153 			self.iframeHTML = settings.doctype + '<html><head>';
27154 
27155 			// We only need to override paths if we have to
27156 			// IE has a bug where it remove site absolute urls to relative ones if this is specified
27157 			if (settings.document_base_url != self.documentBaseUrl) {
27158 				self.iframeHTML += '<base href="' + self.documentBaseURI.getURI() + '" />';
27159 			}
27160 
27161 			// IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
27162 			if (!Env.caretAfter && settings.ie7_compat) {
27163 				self.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
27164 			}
27165 
27166 			self.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
27167 
27168 			// Load the CSS by injecting them into the HTML this will reduce "flicker"
27169 			for (i = 0; i < self.contentCSS.length; i++) {
27170 				var cssUrl = self.contentCSS[i];
27171 				self.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + cssUrl + '" />';
27172 				self.loadedCSS[cssUrl] = true;
27173 			}
27174 
27175 			bodyId = settings.body_id || 'tinymce';
27176 			if (bodyId.indexOf('=') != -1) {
27177 				bodyId = self.getParam('body_id', '', 'hash');
27178 				bodyId = bodyId[self.id] || bodyId;
27179 			}
27180 
27181 			bodyClass = settings.body_class || '';
27182 			if (bodyClass.indexOf('=') != -1) {
27183 				bodyClass = self.getParam('body_class', '', 'hash');
27184 				bodyClass = bodyClass[self.id] || '';
27185 			}
27186 
27187 			self.iframeHTML += '</head><body id="' + bodyId + '" class="mce-content-body ' + bodyClass + '" ' +
27188 				'onload="window.parent.tinymce.get(\'' + self.id + '\').fire(\'load\');"><br></body></html>';
27189 
27190 			/*eslint no-script-url:0 */
27191 			var domainRelaxUrl = 'javascript:(function(){' +
27192 				'document.open();document.domain="' + document.domain + '";' +
27193 				'var ed = window.parent.tinymce.get("' + self.id + '");document.write(ed.iframeHTML);' +
27194 				'document.close();ed.initContentBody(true);})()';
27195 
27196 			// Domain relaxing is required since the user has messed around with document.domain
27197 			if (document.domain != location.hostname) {
27198 				url = domainRelaxUrl;
27199 			}
27200 
27201 			// Create iframe
27202 			// TODO: ACC add the appropriate description on this.
27203 			n = DOM.add(o.iframeContainer, 'iframe', {
27204 				id: self.id + "_ifr",
27205 				src: url || 'javascript:""', // Workaround for HTTPS warning in IE6/7
27206 				frameBorder: '0',
27207 				allowTransparency: "true",
27208 				title: self.editorManager.translate(
27209 					"Rich Text Area. Press ALT-F9 for menu. " +
27210 					"Press ALT-F10 for toolbar. Press ALT-0 for help"
27211 				),
27212 				style: {
27213 					width: '100%',
27214 					height: h,
27215 					display: 'block' // Important for Gecko to render the iframe correctly
27216 				}
27217 			});
27218 
27219 			// Try accessing the document this will fail on IE when document.domain is set to the same as location.hostname
27220 			// Then we have to force domain relaxing using the domainRelaxUrl approach very ugly!!
27221 			if (ie) {
27222 				try {
27223 					self.getDoc();
27224 				} catch (e) {
27225 					n.src = url = domainRelaxUrl;
27226 				}
27227 			}
27228 
27229 			self.contentAreaContainer = o.iframeContainer;
27230 
27231 			if (o.editorContainer) {
27232 				DOM.get(o.editorContainer).style.display = self.orgDisplay;
27233 			}
27234 
27235 			DOM.get(self.id).style.display = 'none';
27236 			DOM.setAttrib(self.id, 'aria-hidden', true);
27237 
27238 			if (!url) {
27239 				self.initContentBody();
27240 			}
27241 
27242 			elm = n = o = null; // Cleanup
27243 		},
27244 
27245 		/**
27246 		 * This method get called by the init method ones the iframe is loaded.
27247 		 * It will fill the iframe with contents, setups DOM and selection objects for the iframe.
27248 		 *
27249 		 * @method initContentBody
27250 		 * @private
27251 		 */
27252 		initContentBody: function(skipWrite) {
27253 			var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), body, contentCssText;
27254 
27255 			// Restore visibility on target element
27256 			if (!settings.inline) {
27257 				self.getElement().style.visibility = self.orgVisibility;
27258 			}
27259 
27260 			// Setup iframe body
27261 			if (!skipWrite && !settings.content_editable) {
27262 				doc.open();
27263 				doc.write(self.iframeHTML);
27264 				doc.close();
27265 			}
27266 
27267 			if (settings.content_editable) {
27268 				self.on('remove', function() {
27269 					var bodyEl = this.getBody();
27270 
27271 					DOM.removeClass(bodyEl, 'mce-content-body');
27272 					DOM.removeClass(bodyEl, 'mce-edit-focus');
27273 					DOM.setAttrib(bodyEl, 'contentEditable', null);
27274 				});
27275 
27276 				DOM.addClass(targetElm, 'mce-content-body');
27277 				self.contentDocument = doc = settings.content_document || document;
27278 				self.contentWindow = settings.content_window || window;
27279 				self.bodyElement = targetElm;
27280 
27281 				// Prevent leak in IE
27282 				settings.content_document = settings.content_window = null;
27283 
27284 				// TODO: Fix this
27285 				settings.root_name = targetElm.nodeName.toLowerCase();
27286 			}
27287 
27288 			// It will not steal focus while setting contentEditable
27289 			body = self.getBody();
27290 			body.disabled = true;
27291 
27292 			if (!settings.readonly) {
27293 				if (self.inline && DOM.getStyle(body, 'position', true) == 'static') {
27294 					body.style.position = 'relative';
27295 				}
27296 
27297 				body.contentEditable = self.getParam('content_editable_state', true);
27298 			}
27299 
27300 			body.disabled = false;
27301 
27302 			/**
27303 			 * Schema instance, enables you to validate elements and it's children.
27304 			 *
27305 			 * @property schema
27306 			 * @type tinymce.html.Schema
27307 			 */
27308 			self.schema = new Schema(settings);
27309 
27310 			/**
27311 			 * DOM instance for the editor.
27312 			 *
27313 			 * @property dom
27314 			 * @type tinymce.dom.DOMUtils
27315 			 * @example
27316 			 * // Adds a class to all paragraphs within the editor
27317 			 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
27318 			 */
27319 			self.dom = new DOMUtils(doc, {
27320 				keep_values: true,
27321 				url_converter: self.convertURL,
27322 				url_converter_scope: self,
27323 				hex_colors: settings.force_hex_style_colors,
27324 				class_filter: settings.class_filter,
27325 				update_styles: true,
27326 				root_element: settings.content_editable ? self.id : null,
27327 				collect: settings.content_editable,
27328 				schema: self.schema,
27329 				onSetAttrib: function(e) {
27330 					self.fire('SetAttrib', e);
27331 				}
27332 			});
27333 
27334 			/**
27335 			 * HTML parser will be used when contents is inserted into the editor.
27336 			 *
27337 			 * @property parser
27338 			 * @type tinymce.html.DomParser
27339 			 */
27340 			self.parser = new DomParser(settings, self.schema);
27341 
27342 			// Convert src and href into data-mce-src, data-mce-href and data-mce-style
27343 			self.parser.addAttributeFilter('src,href,style,tabindex', function(nodes, name) {
27344 				var i = nodes.length, node, dom = self.dom, value, internalName;
27345 
27346 				while (i--) {
27347 					node = nodes[i];
27348 					value = node.attr(name);
27349 					internalName = 'data-mce-' + name;
27350 
27351 					// Add internal attribute if we need to we don't on a refresh of the document
27352 					if (!node.attributes.map[internalName]) {
27353 						if (name === "style") {
27354 							node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));
27355 						} else if (name === "tabindex") {
27356 							node.attr(internalName, value);
27357 							node.attr(name, null);
27358 						} else {
27359 							node.attr(internalName, self.convertURL(value, name, node.name));
27360 						}
27361 					}
27362 				}
27363 			});
27364 
27365 			// Keep scripts from executing
27366 			self.parser.addNodeFilter('script', function(nodes) {
27367 				var i = nodes.length, node;
27368 
27369 				while (i--) {
27370 					node = nodes[i];
27371 					node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));
27372 				}
27373 			});
27374 
27375 			self.parser.addNodeFilter('#cdata', function(nodes) {
27376 				var i = nodes.length, node;
27377 
27378 				while (i--) {
27379 					node = nodes[i];
27380 					node.type = 8;
27381 					node.name = '#comment';
27382 					node.value = '[CDATA[' + node.value + ']]';
27383 				}
27384 			});
27385 
27386 			self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes) {
27387 				var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
27388 
27389 				while (i--) {
27390 					node = nodes[i];
27391 
27392 					if (node.isEmpty(nonEmptyElements)) {
27393 						node.empty().append(new Node('br', 1)).shortEnded = true;
27394 					}
27395 				}
27396 			});
27397 
27398 			/**
27399 			 * DOM serializer for the editor. Will be used when contents is extracted from the editor.
27400 			 *
27401 			 * @property serializer
27402 			 * @type tinymce.dom.Serializer
27403 			 * @example
27404 			 * // Serializes the first paragraph in the editor into a string
27405 			 * tinymce.activeEditor.serializer.serialize(tinymce.activeEditor.dom.select('p')[0]);
27406 			 */
27407 			self.serializer = new DomSerializer(settings, self);
27408 
27409 			/**
27410 			 * Selection instance for the editor.
27411 			 *
27412 			 * @property selection
27413 			 * @type tinymce.dom.Selection
27414 			 * @example
27415 			 * // Sets some contents to the current selection in the editor
27416 			 * tinymce.activeEditor.selection.setContent('Some contents');
27417 			 *
27418 			 * // Gets the current selection
27419 			 * alert(tinymce.activeEditor.selection.getContent());
27420 			 *
27421 			 * // Selects the first paragraph found
27422 			 * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
27423 			 */
27424 			self.selection = new Selection(self.dom, self.getWin(), self.serializer, self);
27425 
27426 			/**
27427 			 * Formatter instance.
27428 			 *
27429 			 * @property formatter
27430 			 * @type tinymce.Formatter
27431 			 */
27432 			self.formatter = new Formatter(self);
27433 
27434 			/**
27435 			 * Undo manager instance, responsible for handling undo levels.
27436 			 *
27437 			 * @property undoManager
27438 			 * @type tinymce.UndoManager
27439 			 * @example
27440 			 * // Undoes the last modification to the editor
27441 			 * tinymce.activeEditor.undoManager.undo();
27442 			 */
27443 			self.undoManager = new UndoManager(self);
27444 
27445 			self.forceBlocks = new ForceBlocks(self);
27446 			self.enterKey = new EnterKey(self);
27447 			self.editorCommands = new EditorCommands(self);
27448 
27449 			self.fire('PreInit');
27450 
27451 			if (!settings.browser_spellcheck && !settings.gecko_spellcheck) {
27452 				doc.body.spellcheck = false; // Gecko
27453 				DOM.setAttrib(body, "spellcheck", "false");
27454 			}
27455 
27456 			self.fire('PostRender');
27457 
27458 			self.quirks = Quirks(self);
27459 
27460 			if (settings.directionality) {
27461 				body.dir = settings.directionality;
27462 			}
27463 
27464 			if (settings.nowrap) {
27465 				body.style.whiteSpace = "nowrap";
27466 			}
27467 
27468 			if (settings.protect) {
27469 				self.on('BeforeSetContent', function(e) {
27470 					each(settings.protect, function(pattern) {
27471 						e.content = e.content.replace(pattern, function(str) {
27472 							return '<!--mce:protected ' + escape(str) + '-->';
27473 						});
27474 					});
27475 				});
27476 			}
27477 
27478 			self.on('SetContent', function() {
27479 				self.addVisual(self.getBody());
27480 			});
27481 
27482 			// Remove empty contents
27483 			if (settings.padd_empty_editor) {
27484 				self.on('PostProcess', function(e) {
27485 					e.content = e.content.replace(/^(<p[^>]*>( | |\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
27486 				});
27487 			}
27488 
27489 			self.load({initial: true, format: 'html'});
27490 			self.startContent = self.getContent({format: 'raw'});
27491 
27492 			/**
27493 			 * Is set to true after the editor instance has been initialized
27494 			 *
27495 			 * @property initialized
27496 			 * @type Boolean
27497 			 * @example
27498 			 * function isEditorInitialized(editor) {
27499 			 *     return editor && editor.initialized;
27500 			 * }
27501 			 */
27502 			self.initialized = true;
27503 			self.bindPendingEventDelegates();
27504 
27505 			self.fire('init');
27506 			self.focus(true);
27507 			self.nodeChanged({initial: true});
27508 			self.execCallback('init_instance_callback', self);
27509 
27510 			// Add editor specific CSS styles
27511 			if (self.contentStyles.length > 0) {
27512 				contentCssText = '';
27513 
27514 				each(self.contentStyles, function(style) {
27515 					contentCssText += style + "\r\n";
27516 				});
27517 
27518 				self.dom.addStyle(contentCssText);
27519 			}
27520 
27521 			// Load specified content CSS last
27522 			each(self.contentCSS, function(cssUrl) {
27523 				if (!self.loadedCSS[cssUrl]) {
27524 					self.dom.loadCSS(cssUrl);
27525 					self.loadedCSS[cssUrl] = true;
27526 				}
27527 			});
27528 
27529 			// Handle auto focus
27530 			if (settings.auto_focus) {
27531 				setTimeout(function () {
27532 					var ed = self.editorManager.get(settings.auto_focus);
27533 
27534 					ed.selection.select(ed.getBody(), 1);
27535 					ed.selection.collapse(1);
27536 					ed.getBody().focus();
27537 					ed.getWin().focus();
27538 				}, 100);
27539 			}
27540 
27541 			// Clean up references for IE
27542 			targetElm = doc = body = null;
27543 		},
27544 
27545 		/**
27546 		 * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
27547 		 * it will also place DOM focus inside the editor.
27548 		 *
27549 		 * @method focus
27550 		 * @param {Boolean} skip_focus Skip DOM focus. Just set is as the active editor.
27551 		 */
27552 		focus: function(skip_focus) {
27553 			var oed, self = this, selection = self.selection, contentEditable = self.settings.content_editable, rng;
27554 			var controlElm, doc = self.getDoc(), body;
27555 
27556 			if (!skip_focus) {
27557 				// Get selected control element
27558 				rng = selection.getRng();
27559 				if (rng.item) {
27560 					controlElm = rng.item(0);
27561 				}
27562 
27563 				self._refreshContentEditable();
27564 
27565 				// Focus the window iframe
27566 				if (!contentEditable) {
27567 					// WebKit needs this call to fire focusin event properly see #5948
27568 					// But Opera pre Blink engine will produce an empty selection so skip Opera
27569 					if (!Env.opera) {
27570 						self.getBody().focus();
27571 					}
27572 
27573 					self.getWin().focus();
27574 				}
27575 
27576 				// Focus the body as well since it's contentEditable
27577 				if (isGecko || contentEditable) {
27578 					body = self.getBody();
27579 
27580 					// Check for setActive since it doesn't scroll to the element
27581 					if (body.setActive) {
27582 						// IE 11 sometimes throws "Invalid function" then fallback to focus
27583 						try {
27584 							body.setActive();
27585 						} catch (ex) {
27586 							body.focus();
27587 						}
27588 					} else {
27589 						body.focus();
27590 					}
27591 
27592 					if (contentEditable) {
27593 						selection.normalize();
27594 					}
27595 				}
27596 
27597 				// Restore selected control element
27598 				// This is needed when for example an image is selected within a
27599 				// layer a call to focus will then remove the control selection
27600 				if (controlElm && controlElm.ownerDocument == doc) {
27601 					rng = doc.body.createControlRange();
27602 					rng.addElement(controlElm);
27603 					rng.select();
27604 				}
27605 			}
27606 
27607 			if (self.editorManager.activeEditor != self) {
27608 				if ((oed = self.editorManager.activeEditor)) {
27609 					oed.fire('deactivate', {relatedTarget: self});
27610 				}
27611 
27612 				self.fire('activate', {relatedTarget: oed});
27613 			}
27614 
27615 			self.editorManager.activeEditor = self;
27616 		},
27617 
27618 		/**
27619 		 * Executes a legacy callback. This method is useful to call old 2.x option callbacks.
27620 		 * There new event model is a better way to add callback so this method might be removed in the future.
27621 		 *
27622 		 * @method execCallback
27623 		 * @param {String} name Name of the callback to execute.
27624 		 * @return {Object} Return value passed from callback function.
27625 		 */
27626 		execCallback: function(name) {
27627 			var self = this, callback = self.settings[name], scope;
27628 
27629 			if (!callback) {
27630 				return;
27631 			}
27632 
27633 			// Look through lookup
27634 			if (self.callbackLookup && (scope = self.callbackLookup[name])) {
27635 				callback = scope.func;
27636 				scope = scope.scope;
27637 			}
27638 
27639 			if (typeof(callback) === 'string') {
27640 				scope = callback.replace(/\.\w+$/, '');
27641 				scope = scope ? resolve(scope) : 0;
27642 				callback = resolve(callback);
27643 				self.callbackLookup = self.callbackLookup || {};
27644 				self.callbackLookup[name] = {func: callback, scope: scope};
27645 			}
27646 
27647 			return callback.apply(scope || self, Array.prototype.slice.call(arguments, 1));
27648 		},
27649 
27650 		/**
27651 		 * Translates the specified string by replacing variables with language pack items it will also check if there is
27652 		 * a key mathcin the input.
27653 		 *
27654 		 * @method translate
27655 		 * @param {String} text String to translate by the language pack data.
27656 		 * @return {String} Translated string.
27657 		 */
27658 		translate: function(text) {
27659 			var lang = this.settings.language || 'en', i18n = this.editorManager.i18n;
27660 
27661 			if (!text) {
27662 				return '';
27663 			}
27664 
27665 			return i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) {
27666 				return i18n.data[lang + '.' + b] || '{#' + b + '}';
27667 			});
27668 		},
27669 
27670 		/**
27671 		 * Returns a language pack item by name/key.
27672 		 *
27673 		 * @method getLang
27674 		 * @param {String} name Name/key to get from the language pack.
27675 		 * @param {String} defaultVal Optional default value to retrive.
27676 		 */
27677 		getLang: function(name, defaultVal) {
27678 			return (
27679 				this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] ||
27680 				(defaultVal !== undefined ? defaultVal : '{#' + name + '}')
27681 			);
27682 		},
27683 
27684 		/**
27685 		 * Returns a configuration parameter by name.
27686 		 *
27687 		 * @method getParam
27688 		 * @param {String} name Configruation parameter to retrive.
27689 		 * @param {String} defaultVal Optional default value to return.
27690 		 * @param {String} type Optional type parameter.
27691 		 * @return {String} Configuration parameter value or default value.
27692 		 * @example
27693 		 * // Returns a specific config value from the currently active editor
27694 		 * var someval = tinymce.activeEditor.getParam('myvalue');
27695 		 *
27696 		 * // Returns a specific config value from a specific editor instance by id
27697 		 * var someval2 = tinymce.get('my_editor').getParam('myvalue');
27698 		 */
27699 		getParam: function(name, defaultVal, type) {
27700 			var value = name in this.settings ? this.settings[name] : defaultVal, output;
27701 
27702 			if (type === 'hash') {
27703 				output = {};
27704 
27705 				if (typeof(value) === 'string') {
27706 					each(value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','), function(value) {
27707 						value = value.split('=');
27708 
27709 						if (value.length > 1) {
27710 							output[trim(value[0])] = trim(value[1]);
27711 						} else {
27712 							output[trim(value[0])] = trim(value);
27713 						}
27714 					});
27715 				} else {
27716 					output = value;
27717 				}
27718 
27719 				return output;
27720 			}
27721 
27722 			return value;
27723 		},
27724 
27725 		/**
27726 		 * Distpaches out a onNodeChange event to all observers. This method should be called when you
27727 		 * need to update the UI states or element path etc.
27728 		 *
27729 		 * @method nodeChanged
27730 		 */
27731 		nodeChanged: function() {
27732 			var self = this, selection = self.selection, node, parents, root;
27733 
27734 			// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
27735 			if (self.initialized && !self.settings.disable_nodechange && !self.settings.readonly) {
27736 				// Get start node
27737 				root = self.getBody();
27738 				node = selection.getStart() || root;
27739 				node = ie && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state
27740 
27741 				// Edge case for <p>|<img></p>
27742 				if (node.nodeName == 'IMG' && selection.isCollapsed()) {
27743 					node = node.parentNode;
27744 				}
27745 
27746 				// Get parents and add them to object
27747 				parents = [];
27748 				self.dom.getParent(node, function(node) {
27749 					if (node === root) {
27750 						return true;
27751 					}
27752 
27753 					parents.push(node);
27754 				});
27755 
27756 				self.fire('NodeChange', {element: node, parents: parents});
27757 			}
27758 		},
27759 
27760 		/**
27761 		 * Adds a button that later gets created by the theme in the editors toolbars.
27762 		 *
27763 		 * @method addButton
27764 		 * @param {String} name Button name to add.
27765 		 * @param {Object} settings Settings object with title, cmd etc.
27766 		 * @example
27767 		 * // Adds a custom button to the editor that inserts contents when clicked
27768 		 * tinymce.init({
27769 		 *    ...
27770 		 *
27771 		 *    toolbar: 'example'
27772 		 *
27773 		 *    setup: function(ed) {
27774 		 *       ed.addButton('example', {
27775 		 *          title: 'My title',
27776 		 *          image: '../js/tinymce/plugins/example/img/example.gif',
27777 		 *          onclick: function() {
27778 		 *             ed.insertContent('Hello world!!');
27779 		 *          }
27780 		 *       });
27781 		 *    }
27782 		 * });
27783 		 */
27784 		addButton: function(name, settings) {
27785 			var self = this;
27786 
27787 			if (settings.cmd) {
27788 				settings.onclick = function() {
27789 					self.execCommand(settings.cmd);
27790 				};
27791 			}
27792 
27793 			if (!settings.text && !settings.icon) {
27794 				settings.icon = name;
27795 			}
27796 
27797 			self.buttons = self.buttons || {};
27798 			settings.tooltip = settings.tooltip || settings.title;
27799 			self.buttons[name] = settings;
27800 		},
27801 
27802 		/**
27803 		 * Adds a menu item to be used in the menus of the theme. There might be multiple instances
27804 		 * of this menu item for example it might be used in the main menus of the theme but also in
27805 		 * the context menu so make sure that it's self contained and supports multiple instances.
27806 		 *
27807 		 * @method addMenuItem
27808 		 * @param {String} name Menu item name to add.
27809 		 * @param {Object} settings Settings object with title, cmd etc.
27810 		 * @example
27811 		 * // Adds a custom menu item to the editor that inserts contents when clicked
27812 		 * // The context option allows you to add the menu item to an existing default menu
27813 		 * tinymce.init({
27814 		 *    ...
27815 		 *
27816 		 *    setup: function(ed) {
27817 		 *       ed.addMenuItem('example', {
27818 		 *          text: 'My menu item',
27819 		 *          context: 'tools',
27820 		 *          onclick: function() {
27821 		 *             ed.insertContent('Hello world!!');
27822 		 *          }
27823 		 *       });
27824 		 *    }
27825 		 * });
27826 		 */
27827 		addMenuItem: function(name, settings) {
27828 			var self = this;
27829 
27830 			if (settings.cmd) {
27831 				settings.onclick = function() {
27832 					self.execCommand(settings.cmd);
27833 				};
27834 			}
27835 
27836 			self.menuItems = self.menuItems || {};
27837 			self.menuItems[name] = settings;
27838 		},
27839 
27840 		/**
27841 		 * Adds a custom command to the editor, you can also override existing commands with this method.
27842 		 * The command that you add can be executed with execCommand.
27843 		 *
27844 		 * @method addCommand
27845 		 * @param {String} name Command name to add/override.
27846 		 * @param {addCommandCallback} callback Function to execute when the command occurs.
27847 		 * @param {Object} scope Optional scope to execute the function in.
27848 		 * @example
27849 		 * // Adds a custom command that later can be executed using execCommand
27850 		 * tinymce.init({
27851 		 *    ...
27852 		 *
27853 		 *    setup: function(ed) {
27854 		 *       // Register example command
27855 		 *       ed.addCommand('mycommand', function(ui, v) {
27856 		 *          ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format: 'text'}));
27857 		 *       });
27858 		 *    }
27859 		 * });
27860 		 */
27861 		addCommand: function(name, callback, scope) {
27862 			/**
27863 			 * Callback function that gets called when a command is executed.
27864 			 *
27865 			 * @callback addCommandCallback
27866 			 * @param {Boolean} ui Display UI state true/false.
27867 			 * @param {Object} value Optional value for command.
27868 			 * @return {Boolean} True/false state if the command was handled or not.
27869 			 */
27870 			this.execCommands[name] = {func: callback, scope: scope || this};
27871 		},
27872 
27873 		/**
27874 		 * Adds a custom query state command to the editor, you can also override existing commands with this method.
27875 		 * The command that you add can be executed with queryCommandState function.
27876 		 *
27877 		 * @method addQueryStateHandler
27878 		 * @param {String} name Command name to add/override.
27879 		 * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrival occurs.
27880 		 * @param {Object} scope Optional scope to execute the function in.
27881 		 */
27882 		addQueryStateHandler: function(name, callback, scope) {
27883 			/**
27884 			 * Callback function that gets called when a queryCommandState is executed.
27885 			 *
27886 			 * @callback addQueryStateHandlerCallback
27887 			 * @return {Boolean} True/false state if the command is enabled or not like is it bold.
27888 			 */
27889 			this.queryStateCommands[name] = {func: callback, scope: scope || this};
27890 		},
27891 
27892 		/**
27893 		 * Adds a custom query value command to the editor, you can also override existing commands with this method.
27894 		 * The command that you add can be executed with queryCommandValue function.
27895 		 *
27896 		 * @method addQueryValueHandler
27897 		 * @param {String} name Command name to add/override.
27898 		 * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrival occurs.
27899 		 * @param {Object} scope Optional scope to execute the function in.
27900 		 */
27901 		addQueryValueHandler: function(name, callback, scope) {
27902 			/**
27903 			 * Callback function that gets called when a queryCommandValue is executed.
27904 			 *
27905 			 * @callback addQueryValueHandlerCallback
27906 			 * @return {Object} Value of the command or undefined.
27907 			 */
27908 			this.queryValueCommands[name] = {func: callback, scope: scope || this};
27909 		},
27910 
27911 		/**
27912 		 * Adds a keyboard shortcut for some command or function.
27913 		 *
27914 		 * @method addShortcut
27915 		 * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
27916 		 * @param {String} desc Text description for the command.
27917 		 * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
27918 		 * @param {Object} sc Optional scope to execute the function in.
27919 		 * @return {Boolean} true/false state if the shortcut was added or not.
27920 		 */
27921 		addShortcut: function(pattern, desc, cmdFunc, scope) {
27922 			this.shortcuts.add(pattern, desc, cmdFunc, scope);
27923 		},
27924 
27925 		/**
27926 		 * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or
27927 		 * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org.
27928 		 * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these
27929 		 * return true it will handle the command as a internal browser command.
27930 		 *
27931 		 * @method execCommand
27932 		 * @param {String} cmd Command name to execute, for example mceLink or Bold.
27933 		 * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not.
27934 		 * @param {mixed} value Optional command value, this can be anything.
27935 		 * @param {Object} a Optional arguments object.
27936 		 */
27937 		execCommand: function(cmd, ui, value, args) {
27938 			var self = this, state = 0, cmdItem;
27939 
27940 			if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(cmd) && (!args || !args.skip_focus)) {
27941 				self.focus();
27942 			}
27943 
27944 			args = extend({}, args);
27945 			args = self.fire('BeforeExecCommand', {command: cmd, ui: ui, value: value});
27946 			if (args.isDefaultPrevented()) {
27947 				return false;
27948 			}
27949 
27950 			// Registred commands
27951 			if ((cmdItem = self.execCommands[cmd])) {
27952 				// Fall through on true
27953 				if (cmdItem.func.call(cmdItem.scope, ui, value) !== true) {
27954 					self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
27955 					return true;
27956 				}
27957 			}
27958 
27959 			// Plugin commands
27960 			each(self.plugins, function(p) {
27961 				if (p.execCommand && p.execCommand(cmd, ui, value)) {
27962 					self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
27963 					state = true;
27964 					return false;
27965 				}
27966 			});
27967 
27968 			if (state) {
27969 				return state;
27970 			}
27971 
27972 			// Theme commands
27973 			if (self.theme && self.theme.execCommand && self.theme.execCommand(cmd, ui, value)) {
27974 				self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
27975 				return true;
27976 			}
27977 
27978 			// Editor commands
27979 			if (self.editorCommands.execCommand(cmd, ui, value)) {
27980 				self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
27981 				return true;
27982 			}
27983 
27984 			// Browser commands
27985 			self.getDoc().execCommand(cmd, ui, value);
27986 			self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
27987 		},
27988 
27989 		/**
27990 		 * Returns a command specific state, for example if bold is enabled or not.
27991 		 *
27992 		 * @method queryCommandState
27993 		 * @param {string} cmd Command to query state from.
27994 		 * @return {Boolean} Command specific state, for example if bold is enabled or not.
27995 		 */
27996 		queryCommandState: function(cmd) {
27997 			var self = this, queryItem, returnVal;
27998 
27999 			// Is hidden then return undefined
28000 			if (self._isHidden()) {
28001 				return;
28002 			}
28003 
28004 			// Registred commands
28005 			if ((queryItem = self.queryStateCommands[cmd])) {
28006 				returnVal = queryItem.func.call(queryItem.scope);
28007 
28008 				// Fall though on true
28009 				if (returnVal !== true) {
28010 					return returnVal;
28011 				}
28012 			}
28013 
28014 			// Editor commands
28015 			returnVal = self.editorCommands.queryCommandState(cmd);
28016 			if (returnVal !== -1) {
28017 				return returnVal;
28018 			}
28019 
28020 			// Browser commands
28021 			try {
28022 				return self.getDoc().queryCommandState(cmd);
28023 			} catch (ex) {
28024 				// Fails sometimes see bug: 1896577
28025 			}
28026 		},
28027 
28028 		/**
28029 		 * Returns a command specific value, for example the current font size.
28030 		 *
28031 		 * @method queryCommandValue
28032 		 * @param {string} cmd Command to query value from.
28033 		 * @return {Object} Command specific value, for example the current font size.
28034 		 */
28035 		queryCommandValue: function(cmd) {
28036 			var self = this, queryItem, returnVal;
28037 
28038 			// Is hidden then return undefined
28039 			if (self._isHidden()) {
28040 				return;
28041 			}
28042 
28043 			// Registred commands
28044 			if ((queryItem = self.queryValueCommands[cmd])) {
28045 				returnVal = queryItem.func.call(queryItem.scope);
28046 
28047 				// Fall though on true
28048 				if (returnVal !== true) {
28049 					return returnVal;
28050 				}
28051 			}
28052 
28053 			// Editor commands
28054 			returnVal = self.editorCommands.queryCommandValue(cmd);
28055 			if (returnVal !== undefined) {
28056 				return returnVal;
28057 			}
28058 
28059 			// Browser commands
28060 			try {
28061 				return self.getDoc().queryCommandValue(cmd);
28062 			} catch (ex) {
28063 				// Fails sometimes see bug: 1896577
28064 			}
28065 		},
28066 
28067 		/**
28068 		 * Shows the editor and hides any textarea/div that the editor is supposed to replace.
28069 		 *
28070 		 * @method show
28071 		 */
28072 		show: function() {
28073 			var self = this;
28074 
28075 			if (self.hidden) {
28076 				self.hidden = false;
28077 
28078 				if (self.inline) {
28079 					self.getBody().contentEditable = true;
28080 				} else {
28081 					DOM.show(self.getContainer());
28082 					DOM.hide(self.id);
28083 				}
28084 
28085 				self.load();
28086 				self.fire('show');
28087 			}
28088 		},
28089 
28090 		/**
28091 		 * Hides the editor and shows any textarea/div that the editor is supposed to replace.
28092 		 *
28093 		 * @method hide
28094 		 */
28095 		hide: function() {
28096 			var self = this, doc = self.getDoc();
28097 
28098 			if (!self.hidden) {
28099 				self.hidden = true;
28100 
28101 				// Fixed bug where IE has a blinking cursor left from the editor
28102 				if (ie && doc && !self.inline) {
28103 					doc.execCommand('SelectAll');
28104 				}
28105 
28106 				// We must save before we hide so Safari doesn't crash
28107 				self.save();
28108 
28109 				if (self.inline) {
28110 					self.getBody().contentEditable = false;
28111 
28112 					// Make sure the editor gets blurred
28113 					if (self == self.editorManager.focusedEditor) {
28114 						self.editorManager.focusedEditor = null;
28115 					}
28116 				} else {
28117 					DOM.hide(self.getContainer());
28118 					DOM.setStyle(self.id, 'display', self.orgDisplay);
28119 				}
28120 
28121 				self.fire('hide');
28122 			}
28123 		},
28124 
28125 		/**
28126 		 * Returns true/false if the editor is hidden or not.
28127 		 *
28128 		 * @method isHidden
28129 		 * @return {Boolean} True/false if the editor is hidden or not.
28130 		 */
28131 		isHidden: function() {
28132 			return !!this.hidden;
28133 		},
28134 
28135 		/**
28136 		 * Sets the progress state, this will display a throbber/progess for the editor.
28137 		 * This is ideal for asycronous operations like an AJAX save call.
28138 		 *
28139 		 * @method setProgressState
28140 		 * @param {Boolean} state Boolean state if the progress should be shown or hidden.
28141 		 * @param {Number} time Optional time to wait before the progress gets shown.
28142 		 * @return {Boolean} Same as the input state.
28143 		 * @example
28144 		 * // Show progress for the active editor
28145 		 * tinymce.activeEditor.setProgressState(true);
28146 		 * 
28147 		 * // Hide progress for the active editor
28148 		 * tinymce.activeEditor.setProgressState(false);
28149 		 * 
28150 		 * // Show progress after 3 seconds
28151 		 * tinymce.activeEditor.setProgressState(true, 3000);
28152 		 */
28153 		setProgressState: function(state, time) {
28154 			this.fire('ProgressState', {state: state, time: time});
28155 		},
28156 
28157 		/**
28158 		 * Loads contents from the textarea or div element that got converted into an editor instance.
28159 		 * This method will move the contents from that textarea or div into the editor by using setContent
28160 		 * so all events etc that method has will get dispatched as well.
28161 		 *
28162 		 * @method load
28163 		 * @param {Object} args Optional content object, this gets passed around through the whole load process.
28164 		 * @return {String} HTML string that got set into the editor.
28165 		 */
28166 		load: function(args) {
28167 			var self = this, elm = self.getElement(), html;
28168 
28169 			if (elm) {
28170 				args = args || {};
28171 				args.load = true;
28172 
28173 				html = self.setContent(elm.value !== undefined ? elm.value : elm.innerHTML, args);
28174 				args.element = elm;
28175 
28176 				if (!args.no_events) {
28177 					self.fire('LoadContent', args);
28178 				}
28179 
28180 				args.element = elm = null;
28181 
28182 				return html;
28183 			}
28184 		},
28185 
28186 		/**
28187 		 * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance.
28188 		 * This method will move the HTML contents from the editor into that textarea or div by getContent
28189 		 * so all events etc that method has will get dispatched as well.
28190 		 *
28191 		 * @method save
28192 		 * @param {Object} args Optional content object, this gets passed around through the whole save process.
28193 		 * @return {String} HTML string that got set into the textarea/div.
28194 		 */
28195 		save: function(args) {
28196 			var self = this, elm = self.getElement(), html, form;
28197 
28198 			if (!elm || !self.initialized) {
28199 				return;
28200 			}
28201 
28202 			args = args || {};
28203 			args.save = true;
28204 
28205 			args.element = elm;
28206 			html = args.content = self.getContent(args);
28207 
28208 			if (!args.no_events) {
28209 				self.fire('SaveContent', args);
28210 			}
28211 
28212 			html = args.content;
28213 
28214 			if (!/TEXTAREA|INPUT/i.test(elm.nodeName)) {
28215 				// Update DIV element when not in inline mode
28216 				if (!self.inline) {
28217 					elm.innerHTML = html;
28218 				}
28219 
28220 				// Update hidden form element
28221 				if ((form = DOM.getParent(self.id, 'form'))) {
28222 					each(form.elements, function(elm) {
28223 						if (elm.name == self.id) {
28224 							elm.value = html;
28225 							return false;
28226 						}
28227 					});
28228 				}
28229 			} else {
28230 				elm.value = html;
28231 			}
28232 
28233 			args.element = elm = null;
28234 
28235 			if (args.set_dirty !== false) {
28236 				self.isNotDirty = true;
28237 			}
28238 
28239 			return html;
28240 		},
28241 
28242 		/**
28243 		 * Sets the specified content to the editor instance, this will cleanup the content before it gets set using
28244 		 * the different cleanup rules options.
28245 		 *
28246 		 * @method setContent
28247 		 * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well.
28248 		 * @param {Object} args Optional content object, this gets passed around through the whole set process.
28249 		 * @return {String} HTML string that got set into the editor.
28250 		 * @example
28251 		 * // Sets the HTML contents of the activeEditor editor
28252 		 * tinymce.activeEditor.setContent('<span>some</span> html');
28253 		 *
28254 		 * // Sets the raw contents of the activeEditor editor
28255 		 * tinymce.activeEditor.setContent('<span>some</span> html', {format: 'raw'});
28256 		 *
28257 		 * // Sets the content of a specific editor (my_editor in this example)
28258 		 * tinymce.get('my_editor').setContent(data);
28259 		 *
28260 		 * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added
28261 		 * tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'});
28262 		 */
28263 		setContent: function(content, args) {
28264 			var self = this, body = self.getBody(), forcedRootBlockName;
28265 
28266 			// Setup args object
28267 			args = args || {};
28268 			args.format = args.format || 'html';
28269 			args.set = true;
28270 			args.content = content;
28271 
28272 			// Do preprocessing
28273 			if (!args.no_events) {
28274 				self.fire('BeforeSetContent', args);
28275 			}
28276 
28277 			content = args.content;
28278 
28279 			// Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
28280 			// It will also be impossible to place the caret in the editor unless there is a BR element present
28281 			if (content.length === 0 || /^\s+$/.test(content)) {
28282 				forcedRootBlockName = self.settings.forced_root_block;
28283 
28284 				// Check if forcedRootBlock is configured and that the block is a valid child of the body
28285 				if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
28286 					// Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly
28287 					content = ie && ie < 11 ? '' : '<br data-mce-bogus="1">';
28288 					content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content);
28289 				} else if (!ie) {
28290 					// We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret
28291 					content = '<br data-mce-bogus="1">';
28292 				}
28293 
28294 				body.innerHTML = content;
28295 
28296 				self.fire('SetContent', args);
28297 			} else {
28298 				// Parse and serialize the html
28299 				if (args.format !== 'raw') {
28300 					content = new Serializer({}, self.schema).serialize(
28301 						self.parser.parse(content, {isRootContent: true})
28302 					);
28303 				}
28304 
28305 				// Set the new cleaned contents to the editor
28306 				args.content = trim(content);
28307 				self.dom.setHTML(body, args.content);
28308 
28309 				// Do post processing
28310 				if (!args.no_events) {
28311 					self.fire('SetContent', args);
28312 				}
28313 
28314 				// Don't normalize selection if the focused element isn't the body in
28315 				// content editable mode since it will steal focus otherwise
28316 				/*if (!self.settings.content_editable || document.activeElement === self.getBody()) {
28317 					self.selection.normalize();
28318 				}*/
28319 			}
28320 
28321 			return args.content;
28322 		},
28323 
28324 		/**
28325 		 * Gets the content from the editor instance, this will cleanup the content before it gets returned using
28326 		 * the different cleanup rules options.
28327 		 *
28328 		 * @method getContent
28329 		 * @param {Object} args Optional content object, this gets passed around through the whole get process.
28330 		 * @return {String} Cleaned content string, normally HTML contents.
28331 		 * @example
28332 		 * // Get the HTML contents of the currently active editor
28333 		 * console.debug(tinymce.activeEditor.getContent());
28334 		 *
28335 		 * // Get the raw contents of the currently active editor
28336 		 * tinymce.activeEditor.getContent({format: 'raw'});
28337 		 *
28338 		 * // Get content of a specific editor:
28339 		 * tinymce.get('content id').getContent()
28340 		 */
28341 		getContent: function(args) {
28342 			var self = this, content, body = self.getBody();
28343 
28344 			// Setup args object
28345 			args = args || {};
28346 			args.format = args.format || 'html';
28347 			args.get = true;
28348 			args.getInner = true;
28349 
28350 			// Do preprocessing
28351 			if (!args.no_events) {
28352 				self.fire('BeforeGetContent', args);
28353 			}
28354 
28355 			// Get raw contents or by default the cleaned contents
28356 			if (args.format == 'raw') {
28357 				content = body.innerHTML;
28358 			} else if (args.format == 'text') {
28359 				content = body.innerText || body.textContent;
28360 			} else {
28361 				content = self.serializer.serialize(body, args);
28362 			}
28363 
28364 			// Trim whitespace in beginning/end of HTML
28365 			if (args.format != 'text') {
28366 				args.content = trim(content);
28367 			} else {
28368 				args.content = content;
28369 			}
28370 
28371 			// Do post processing
28372 			if (!args.no_events) {
28373 				self.fire('GetContent', args);
28374 			}
28375 
28376 			return args.content;
28377 		},
28378 
28379 		/**
28380 		 * Inserts content at caret position.
28381 		 *
28382 		 * @method insertContent
28383 		 * @param {String} content Content to insert.
28384 		 */
28385 		insertContent: function(content) {
28386 			this.execCommand('mceInsertContent', false, content);
28387 		},
28388 
28389 		/**
28390 		 * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
28391 		 *
28392 		 * @method isDirty
28393 		 * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents.
28394 		 * @example
28395 		 * if (tinymce.activeEditor.isDirty())
28396 		 *     alert("You must save your contents.");
28397 		 */
28398 		isDirty: function() {
28399 			return !this.isNotDirty;
28400 		},
28401 
28402 		/**
28403 		 * Returns the editors container element. The container element wrappes in
28404 		 * all the elements added to the page for the editor. Such as UI, iframe etc.
28405 		 *
28406 		 * @method getContainer
28407 		 * @return {Element} HTML DOM element for the editor container.
28408 		 */
28409 		getContainer: function() {
28410 			var self = this;
28411 
28412 			if (!self.container) {
28413 				self.container = DOM.get(self.editorContainer || self.id + '_parent');
28414 			}
28415 
28416 			return self.container;
28417 		},
28418 
28419 		/**
28420 		 * Returns the editors content area container element. The this element is the one who
28421 		 * holds the iframe or the editable element.
28422 		 *
28423 		 * @method getContentAreaContainer
28424 		 * @return {Element} HTML DOM element for the editor area container.
28425 		 */
28426 		getContentAreaContainer: function() {
28427 			return this.contentAreaContainer;
28428 		},
28429 
28430 		/**
28431 		 * Returns the target element/textarea that got replaced with a TinyMCE editor instance.
28432 		 *
28433 		 * @method getElement
28434 		 * @return {Element} HTML DOM element for the replaced element.
28435 		 */
28436 		getElement: function() {
28437 			return DOM.get(this.settings.content_element || this.id);
28438 		},
28439 
28440 		/**
28441 		 * Returns the iframes window object.
28442 		 *
28443 		 * @method getWin
28444 		 * @return {Window} Iframe DOM window object.
28445 		 */
28446 		getWin: function() {
28447 			var self = this, elm;
28448 
28449 			if (!self.contentWindow) {
28450 				elm = DOM.get(self.id + "_ifr");
28451 
28452 				if (elm) {
28453 					self.contentWindow = elm.contentWindow;
28454 				}
28455 			}
28456 
28457 			return self.contentWindow;
28458 		},
28459 
28460 		/**
28461 		 * Returns the iframes document object.
28462 		 *
28463 		 * @method getDoc
28464 		 * @return {Document} Iframe DOM document object.
28465 		 */
28466 		getDoc: function() {
28467 			var self = this, win;
28468 
28469 			if (!self.contentDocument) {
28470 				win = self.getWin();
28471 
28472 				if (win) {
28473 					self.contentDocument = win.document;
28474 				}
28475 			}
28476 
28477 			return self.contentDocument;
28478 		},
28479 
28480 		/**
28481 		 * Returns the iframes body element.
28482 		 *
28483 		 * @method getBody
28484 		 * @return {Element} Iframe body element.
28485 		 */
28486 		getBody: function() {
28487 			return this.bodyElement || this.getDoc().body;
28488 		},
28489 
28490 		/**
28491 		 * URL converter function this gets executed each time a user adds an img, a or
28492 		 * any other element that has a URL in it. This will be called both by the DOM and HTML
28493 		 * manipulation functions.
28494 		 *
28495 		 * @method convertURL
28496 		 * @param {string} url URL to convert.
28497 		 * @param {string} name Attribute name src, href etc.
28498 		 * @param {string/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert.
28499 		 * @return {string} Converted URL string.
28500 		 */
28501 		convertURL: function(url, name, elm) {
28502 			var self = this, settings = self.settings;
28503 
28504 			// Use callback instead
28505 			if (settings.urlconverter_callback) {
28506 				return self.execCallback('urlconverter_callback', url, elm, true, name);
28507 			}
28508 
28509 			// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
28510 			if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0 || url.length === 0) {
28511 				return url;
28512 			}
28513 
28514 			// Convert to relative
28515 			if (settings.relative_urls) {
28516 				return self.documentBaseURI.toRelative(url);
28517 			}
28518 
28519 			// Convert to absolute
28520 			url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);
28521 
28522 			return url;
28523 		},
28524 
28525 		/**
28526 		 * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor.
28527 		 *
28528 		 * @method addVisual
28529 		 * @param {Element} elm Optional root element to loop though to find tables etc that needs the visual aid.
28530 		 */
28531 		addVisual: function(elm) {
28532 			var self = this, settings = self.settings, dom = self.dom, cls;
28533 
28534 			elm = elm || self.getBody();
28535 
28536 			if (self.hasVisual === undefined) {
28537 				self.hasVisual = settings.visual;
28538 			}
28539 
28540 			each(dom.select('table,a', elm), function(elm) {
28541 				var value;
28542 
28543 				switch (elm.nodeName) {
28544 					case 'TABLE':
28545 						cls = settings.visual_table_class || 'mce-item-table';
28546 						value = dom.getAttrib(elm, 'border');
28547 
28548 						if (!value || value == '0') {
28549 							if (self.hasVisual) {
28550 								dom.addClass(elm, cls);
28551 							} else {
28552 								dom.removeClass(elm, cls);
28553 							}
28554 						}
28555 
28556 						return;
28557 
28558 					case 'A':
28559 						if (!dom.getAttrib(elm, 'href', false)) {
28560 							value = dom.getAttrib(elm, 'name') || elm.id;
28561 							cls = settings.visual_anchor_class || 'mce-item-anchor';
28562 
28563 							if (value) {
28564 								if (self.hasVisual) {
28565 									dom.addClass(elm, cls);
28566 								} else {
28567 									dom.removeClass(elm, cls);
28568 								}
28569 							}
28570 						}
28571 
28572 						return;
28573 				}
28574 			});
28575 
28576 			self.fire('VisualAid', {element: elm, hasVisual: self.hasVisual});
28577 		},
28578 
28579 		/**
28580 		 * Removes the editor from the dom and tinymce collection.
28581 		 *
28582 		 * @method remove
28583 		 */
28584 		remove: function() {
28585 			var self = this;
28586 
28587 			if (!self.removed) {
28588 				self.removed = 1;
28589 				self.save();
28590 
28591 				// Remove any hidden input
28592 				if (self.hasHiddenInput) {
28593 					DOM.remove(self.getElement().nextSibling);
28594 				}
28595 
28596 				if (!self.inline) {
28597 					// IE 9 has a bug where the selection stops working if you place the
28598 					// caret inside the editor then remove the iframe
28599 					if (ie && ie < 10) {
28600 						self.getDoc().execCommand('SelectAll', false, null);
28601 					}
28602 
28603 					DOM.setStyle(self.id, 'display', self.orgDisplay);
28604 					self.getBody().onload = null; // Prevent #6816
28605 
28606 					// Don't clear the window or document if content editable
28607 					// is enabled since other instances might still be present
28608 					Event.unbind(self.getWin());
28609 					Event.unbind(self.getDoc());
28610 				}
28611 
28612 				var elm = self.getContainer();
28613 				Event.unbind(self.getBody());
28614 				Event.unbind(elm);
28615 
28616 				self.fire('remove');
28617 
28618 				self.editorManager.remove(self);
28619 				DOM.remove(elm);
28620 				self.destroy();
28621 			}
28622 		},
28623 
28624 		/**
28625 		 * Destroys the editor instance by removing all events, element references or other resources
28626 		 * that could leak memory. This method will be called automatically when the page is unloaded
28627 		 * but you can also call it directly if you know what you are doing.
28628 		 *
28629 		 * @method destroy
28630 		 * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one.
28631 		 */
28632 		destroy: function(automatic) {
28633 			var self = this, form;
28634 
28635 			// One time is enough
28636 			if (self.destroyed) {
28637 				return;
28638 			}
28639 
28640 			// If user manually calls destroy and not remove
28641 			// Users seems to have logic that calls destroy instead of remove
28642 			if (!automatic && !self.removed) {
28643 				self.remove();
28644 				return;
28645 			}
28646 
28647 			// We must unbind on Gecko since it would otherwise produce the pesky "attempt
28648 			// to run compile-and-go script on a cleared scope" message
28649 			if (automatic && isGecko) {
28650 				Event.unbind(self.getDoc());
28651 				Event.unbind(self.getWin());
28652 				Event.unbind(self.getBody());
28653 			}
28654 
28655 			if (!automatic) {
28656 				self.editorManager.off('beforeunload', self._beforeUnload);
28657 
28658 				// Manual destroy
28659 				if (self.theme && self.theme.destroy) {
28660 					self.theme.destroy();
28661 				}
28662 
28663 				// Destroy controls, selection and dom
28664 				self.selection.destroy();
28665 				self.dom.destroy();
28666 			}
28667 
28668 			form = self.formElement;
28669 			if (form) {
28670 				if (form._mceOldSubmit) {
28671 					form.submit = form._mceOldSubmit;
28672 					form._mceOldSubmit = null;
28673 				}
28674 
28675 				DOM.unbind(form, 'submit reset', self.formEventDelegate);
28676 			}
28677 
28678 			self.contentAreaContainer = self.formElement = self.container = self.editorContainer = null;
28679 			self.settings.content_element = self.bodyElement = self.contentDocument = self.contentWindow = null;
28680 
28681 			if (self.selection) {
28682 				self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null;
28683 			}
28684 
28685 			self.destroyed = 1;
28686 		},
28687 
28688 		// Internal functions
28689 
28690 		_refreshContentEditable: function() {
28691 			var self = this, body, parent;
28692 
28693 			// Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
28694 			if (self._isHidden()) {
28695 				body = self.getBody();
28696 				parent = body.parentNode;
28697 
28698 				parent.removeChild(body);
28699 				parent.appendChild(body);
28700 
28701 				body.focus();
28702 			}
28703 		},
28704 
28705 		_isHidden: function() {
28706 			var sel;
28707 
28708 			if (!isGecko) {
28709 				return 0;
28710 			}
28711 
28712 			// Weird, wheres that cursor selection?
28713 			sel = this.selection.getSel();
28714 			return (!sel || !sel.rangeCount || sel.rangeCount === 0);
28715 		}
28716 	};
28717 
28718 	extend(Editor.prototype, EditorObservable);
28719 
28720 	return Editor;
28721 });
28722 
28723 // Included from: js/tinymce/classes/util/I18n.js
28724 
28725 /**
28726  * I18n.js
28727  *
28728  * Copyright, Moxiecode Systems AB
28729  * Released under LGPL License.
28730  *
28731  * License: http://www.tinymce.com/license
28732  * Contributing: http://www.tinymce.com/contributing
28733  */
28734 
28735 /**
28736  * I18n class that handles translation of TinyMCE UI.
28737  * Uses po style with csharp style parameters.
28738  *
28739  * @class tinymce.util.I18n
28740  */
28741 define("tinymce/util/I18n", [], function() {
28742 	"use strict";
28743 
28744 	var data = {};
28745 
28746 	return {
28747 		/**
28748 		 * Property gets set to true if a RTL language pack was loaded.
28749 		 *
28750 		 * @property rtl
28751 		 * @type Boolean
28752 		 */
28753 		rtl: false,
28754 
28755 		/**
28756 		 * Adds translations for a specific language code.
28757 		 *
28758 		 * @method add
28759 		 * @param {String} code Language code like sv_SE.
28760 		 * @param {Array} items Name/value array with English en_US to sv_SE.
28761 		 */
28762 		add: function(code, items) {
28763 			for (var name in items) {
28764 				data[name] = items[name];
28765 			}
28766 
28767 			this.rtl = this.rtl || data._dir === 'rtl';
28768 		},
28769 
28770 		/**
28771 		 * Translates the specified text.
28772 		 *
28773 		 * It has a few formats:
28774 		 * I18n.translate("Text");
28775 		 * I18n.translate(["Text {0}/{1}", 0, 1]);
28776 		 * I18n.translate({raw: "Raw string"});
28777 		 *
28778 		 * @method translate
28779 		 * @param {String/Object/Array} text Text to translate.
28780 		 * @return {String} String that got translated.
28781 		 */
28782 		translate: function(text) {
28783 			if (typeof(text) == "undefined") {
28784 				return text;
28785 			}
28786 
28787 			if (typeof(text) != "string" && text.raw) {
28788 				return text.raw;
28789 			}
28790 
28791 			if (text.push) {
28792 				var values = text.slice(1);
28793 
28794 				text = (data[text[0]] || text[0]).replace(/\{([^\}]+)\}/g, function(match1, match2) {
28795 					return values[match2];
28796 				});
28797 			}
28798 
28799 			return data[text] || text;
28800 		},
28801 
28802 		data: data
28803 	};
28804 });
28805 
28806 // Included from: js/tinymce/classes/FocusManager.js
28807 
28808 /**
28809  * FocusManager.js
28810  *
28811  * Copyright, Moxiecode Systems AB
28812  * Released under LGPL License.
28813  *
28814  * License: http://www.tinymce.com/license
28815  * Contributing: http://www.tinymce.com/contributing
28816  */
28817 
28818 /**
28819  * This class manages the focus/blur state of the editor. This class is needed since some
28820  * browsers fire false focus/blur states when the selection is moved to a UI dialog or similar.
28821  *
28822  * This class will fire two events focus and blur on the editor instances that got affected.
28823  * It will also handle the restore of selection when the focus is lost and returned.
28824  *
28825  * @class tinymce.FocusManager
28826  */
28827 define("tinymce/FocusManager", [
28828 	"tinymce/dom/DOMUtils",
28829 	"tinymce/Env"
28830 ], function(DOMUtils, Env) {
28831 	var selectionChangeHandler, documentFocusInHandler, documentMouseUpHandler, DOM = DOMUtils.DOM;
28832 
28833 	/**
28834 	 * Constructs a new focus manager instance.
28835 	 *
28836 	 * @constructor FocusManager
28837 	 * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for.
28838 	 */
28839 	function FocusManager(editorManager) {
28840 		function getActiveElement() {
28841 			try {
28842 				return document.activeElement;
28843 			} catch (ex) {
28844 				// IE sometimes fails to get the activeElement when resizing table
28845 				// TODO: Investigate this
28846 				return document.body;
28847 			}
28848 		}
28849 
28850 		// We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object
28851 		// TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well.
28852 		function createBookmark(dom, rng) {
28853 			if (rng && rng.startContainer) {
28854 				// Verify that the range is within the root of the editor
28855 				if (!dom.isChildOf(rng.startContainer, dom.getRoot()) || !dom.isChildOf(rng.endContainer, dom.getRoot())) {
28856 					return;
28857 				}
28858 
28859 				return {
28860 					startContainer: rng.startContainer,
28861 					startOffset: rng.startOffset,
28862 					endContainer: rng.endContainer,
28863 					endOffset: rng.endOffset
28864 				};
28865 			}
28866 
28867 			return rng;
28868 		}
28869 
28870 		function bookmarkToRng(editor, bookmark) {
28871 			var rng;
28872 
28873 			if (bookmark.startContainer) {
28874 				rng = editor.getDoc().createRange();
28875 				rng.setStart(bookmark.startContainer, bookmark.startOffset);
28876 				rng.setEnd(bookmark.endContainer, bookmark.endOffset);
28877 			} else {
28878 				rng = bookmark;
28879 			}
28880 
28881 			return rng;
28882 		}
28883 
28884 		function isUIElement(elm) {
28885 			return !!DOM.getParent(elm, FocusManager.isEditorUIElement);
28886 		}
28887 
28888 		function registerEvents(e) {
28889 			var editor = e.editor;
28890 
28891 			editor.on('init', function() {
28892 				// Gecko/WebKit has ghost selections in iframes and IE only has one selection per browser tab
28893 				if (editor.inline || Env.ie) {
28894 					// On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes
28895 					editor.on('nodechange keyup', function() {
28896 						var node = document.activeElement;
28897 
28898 						// IE 11 reports active element as iframe not body of iframe
28899 						if (node && node.id == editor.id + '_ifr') {
28900 							node = editor.getBody();
28901 						}
28902 
28903 						if (editor.dom.isChildOf(node, editor.getBody())) {
28904 							editor.lastRng = editor.selection.getRng();
28905 						}
28906 					});
28907 
28908 					// Handles the issue with WebKit not retaining selection within inline document
28909 					// If the user releases the mouse out side the body since a mouse up event wont occur on the body
28910 					if (Env.webkit && !selectionChangeHandler) {
28911 						selectionChangeHandler = function() {
28912 							var activeEditor = editorManager.activeEditor;
28913 
28914 							if (activeEditor && activeEditor.selection) {
28915 								var rng = activeEditor.selection.getRng();
28916 
28917 								// Store when it's non collapsed
28918 								if (rng && !rng.collapsed) {
28919 									editor.lastRng = rng;
28920 								}
28921 							}
28922 						};
28923 
28924 						DOM.bind(document, 'selectionchange', selectionChangeHandler);
28925 					}
28926 				}
28927 			});
28928 
28929 			editor.on('setcontent', function() {
28930 				editor.lastRng = null;
28931 			});
28932 
28933 			// Remove last selection bookmark on mousedown see #6305
28934 			editor.on('mousedown', function() {
28935 				editor.selection.lastFocusBookmark = null;
28936 			});
28937 
28938 			editor.on('focusin', function() {
28939 				var focusedEditor = editorManager.focusedEditor;
28940 
28941 				if (editor.selection.lastFocusBookmark) {
28942 					editor.selection.setRng(bookmarkToRng(editor, editor.selection.lastFocusBookmark));
28943 					editor.selection.lastFocusBookmark = null;
28944 				}
28945 
28946 				if (focusedEditor != editor) {
28947 					if (focusedEditor) {
28948 						focusedEditor.fire('blur', {focusedEditor: editor});
28949 					}
28950 
28951 					editorManager.activeEditor = editor;
28952 					editorManager.focusedEditor = editor;
28953 					editor.fire('focus', {blurredEditor: focusedEditor});
28954 					editor.focus(true);
28955 				}
28956 
28957 				editor.lastRng = null;
28958 			});
28959 
28960 			editor.on('focusout', function() {
28961 				window.setTimeout(function() {
28962 					var focusedEditor = editorManager.focusedEditor;
28963 
28964 					// Still the same editor the the blur was outside any editor UI
28965 					if (!isUIElement(getActiveElement()) && focusedEditor == editor) {
28966 						editor.fire('blur', {focusedEditor: null});
28967 						editorManager.focusedEditor = null;
28968 
28969 						// Make sure selection is valid could be invalid if the editor is blured and removed before the timeout occurs
28970 						if (editor.selection) {
28971 							editor.selection.lastFocusBookmark = null;
28972 						}
28973 					}
28974 				}, 0);
28975 			});
28976 
28977 			// Check if focus is moved to an element outside the active editor by checking if the target node
28978 			// isn't within the body of the activeEditor nor a UI element such as a dialog child control
28979 			if (!documentFocusInHandler) {
28980 				documentFocusInHandler = function(e) {
28981 					var activeEditor = editorManager.activeEditor;
28982 
28983 					if (activeEditor && e.target.ownerDocument == document) {
28984 						// Check to make sure we have a valid selection
28985 						if (activeEditor.selection) {
28986 							activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.dom, activeEditor.lastRng);
28987 						}
28988 
28989 						// Fire a blur event if the element isn't a UI element
28990 						if (!isUIElement(e.target) && editorManager.focusedEditor == activeEditor) {
28991 							activeEditor.fire('blur', {focusedEditor: null});
28992 							editorManager.focusedEditor = null;
28993 						}
28994 					}
28995 				};
28996 
28997 				DOM.bind(document, 'focusin', documentFocusInHandler);
28998 			}
28999 
29000 			// Handle edge case when user starts the selection inside the editor and releases
29001 			// the mouse outside the editor producing a new selection. This weird workaround is needed since
29002 			// Gecko doesn't have the "selectionchange" event we need to do this. Fixes: #6843
29003 			if (editor.inline && !documentMouseUpHandler) {
29004 				documentMouseUpHandler = function(e) {
29005 					var activeEditor = editorManager.activeEditor;
29006 
29007 					if (activeEditor.inline && !activeEditor.dom.isChildOf(e.target, activeEditor.getBody())) {
29008 						var rng = activeEditor.selection.getRng();
29009 
29010 						if (!rng.collapsed) {
29011 							activeEditor.lastRng = rng;
29012 						}
29013 					}
29014 				};
29015 
29016 				DOM.bind(document, 'mouseup', documentMouseUpHandler);
29017 			}
29018 		}
29019 
29020 		function unregisterDocumentEvents(e) {
29021 			if (editorManager.focusedEditor == e.editor) {
29022 				editorManager.focusedEditor = null;
29023 			}
29024 
29025 			if (!editorManager.activeEditor) {
29026 				DOM.unbind(document, 'selectionchange', selectionChangeHandler);
29027 				DOM.unbind(document, 'focusin', documentFocusInHandler);
29028 				DOM.unbind(document, 'mouseup', documentMouseUpHandler);
29029 				selectionChangeHandler = documentFocusInHandler = documentMouseUpHandler = null;
29030 			}
29031 		}
29032 
29033 		editorManager.on('AddEditor', registerEvents);
29034 		editorManager.on('RemoveEditor', unregisterDocumentEvents);
29035 	}
29036 
29037 	/**
29038 	 * Returns true if the specified element is part of the UI for example an button or text input.
29039 	 *
29040 	 * @method isEditorUIElement
29041 	 * @param  {Element} elm Element to check if it's part of the UI or not.
29042 	 * @return {Boolean} True/false state if the element is part of the UI or not.
29043 	 */
29044 	FocusManager.isEditorUIElement = function(elm) {
29045 		// Needs to be converted to string since svg can have focus: #6776
29046 		return elm.className.toString().indexOf('mce-') !== -1;
29047 	};
29048 
29049 	return FocusManager;
29050 });
29051 
29052 // Included from: js/tinymce/classes/EditorManager.js
29053 
29054 /**
29055  * EditorManager.js
29056  *
29057  * Copyright, Moxiecode Systems AB
29058  * Released under LGPL License.
29059  *
29060  * License: http://www.tinymce.com/license
29061  * Contributing: http://www.tinymce.com/contributing
29062  */
29063 
29064 /**
29065  * This class used as a factory for manager for tinymce.Editor instances.
29066  *
29067  * @example
29068  * tinymce.EditorManager.init({});
29069  *
29070  * @class tinymce.EditorManager
29071  * @mixes tinymce.util.Observable
29072  * @static
29073  */
29074 define("tinymce/EditorManager", [
29075 	"tinymce/Editor",
29076 	"tinymce/dom/DOMUtils",
29077 	"tinymce/util/URI",
29078 	"tinymce/Env",
29079 	"tinymce/util/Tools",
29080 	"tinymce/util/Observable",
29081 	"tinymce/util/I18n",
29082 	"tinymce/FocusManager"
29083 ], function(Editor, DOMUtils, URI, Env, Tools, Observable, I18n, FocusManager) {
29084 	var DOM = DOMUtils.DOM;
29085 	var explode = Tools.explode, each = Tools.each, extend = Tools.extend;
29086 	var instanceCounter = 0, beforeUnloadDelegate, EditorManager;
29087 
29088 	function removeEditorFromList(editor) {
29089 		var editors = EditorManager.editors, removedFromList;
29090 
29091 		delete editors[editor.id];
29092 
29093 		for (var i = 0; i < editors.length; i++) {
29094 			if (editors[i] == editor) {
29095 				editors.splice(i, 1);
29096 				removedFromList = true;
29097 				break;
29098 			}
29099 		}
29100 
29101 		// Select another editor since the active one was removed
29102 		if (EditorManager.activeEditor == editor) {
29103 			EditorManager.activeEditor = editors[0];
29104 		}
29105 
29106 		// Clear focusedEditor if necessary, so that we don't try to blur the destroyed editor
29107 		if (EditorManager.focusedEditor == editor) {
29108 			EditorManager.focusedEditor = null;
29109 		}
29110 
29111 		return removedFromList;
29112 	}
29113 
29114 	function purgeDestroyedEditor(editor) {
29115 		// User has manually destroyed the editor lets clean up the mess
29116 		if (editor && !(editor.getContainer() || editor.getBody()).parentNode) {
29117 			removeEditorFromList(editor);
29118 			editor.destroy(true);
29119 			editor = null;
29120 		}
29121 
29122 		return editor;
29123 	}
29124 
29125 	EditorManager = {
29126 		/**
29127 		 * Major version of TinyMCE build.
29128 		 *
29129 		 * @property majorVersion
29130 		 * @type String
29131 		 */
29132 		majorVersion : '4',
29133 
29134 		/**
29135 		 * Minor version of TinyMCE build.
29136 		 *
29137 		 * @property minorVersion
29138 		 * @type String
29139 		 */
29140 		minorVersion : '0.25',
29141 
29142 		/**
29143 		 * Release date of TinyMCE build.
29144 		 *
29145 		 * @property releaseDate
29146 		 * @type String
29147 		 */
29148 		releaseDate: '2014-04-30',
29149 
29150 		/**
29151 		 * Collection of editor instances.
29152 		 *
29153 		 * @property editors
29154 		 * @type Object
29155 		 * @example
29156 		 * for (edId in tinymce.editors)
29157 		 *     tinymce.editors[edId].save();
29158 		 */
29159 		editors: [],
29160 
29161 		/**
29162 		 * Collection of language pack data.
29163 		 *
29164 		 * @property i18n
29165 		 * @type Object
29166 		 */
29167 		i18n: I18n,
29168 
29169 		/**
29170 		 * Currently active editor instance.
29171 		 *
29172 		 * @property activeEditor
29173 		 * @type tinymce.Editor
29174 		 * @example
29175 		 * tinyMCE.activeEditor.selection.getContent();
29176 		 * tinymce.EditorManager.activeEditor.selection.getContent();
29177 		 */
29178 		activeEditor: null,
29179 
29180 		setup: function() {
29181 			var self = this, baseURL, documentBaseURL, suffix = "", preInit, src;
29182 
29183 			// Get base URL for the current document
29184 			documentBaseURL = document.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
29185 			if (!/[\/\\]$/.test(documentBaseURL)) {
29186 				documentBaseURL += '/';
29187 			}
29188 
29189 			// If tinymce is defined and has a base use that or use the old tinyMCEPreInit
29190 			preInit = window.tinymce || window.tinyMCEPreInit;
29191 			if (preInit) {
29192 				baseURL = preInit.base || preInit.baseURL;
29193 				suffix = preInit.suffix;
29194 			} else {
29195 				// Get base where the tinymce script is located
29196 				var scripts = document.getElementsByTagName('script');
29197 				for (var i = 0; i < scripts.length; i++) {
29198 					src = scripts[i].src;
29199 
29200 					// Script types supported:
29201 					// tinymce.js tinymce.min.js tinymce.dev.js
29202 					// tinymce.jquery.js tinymce.jquery.min.js tinymce.jquery.dev.js
29203 					// tinymce.full.js tinymce.full.min.js tinymce.full.dev.js
29204 					if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) {
29205 						if (src.indexOf('.min') != -1) {
29206 							suffix = '.min';
29207 						}
29208 
29209 						baseURL = src.substring(0, src.lastIndexOf('/'));
29210 						break;
29211 					}
29212 				}
29213 
29214 				// We didn't find any baseURL by looking at the script elements
29215 				// Try to use the document.currentScript as a fallback
29216 				if (!baseURL && document.currentScript) {
29217 					src = document.currentScript.src;
29218 
29219 					if (src.indexOf('.min') != -1) {
29220 						suffix = '.min';
29221 					}
29222 
29223 					baseURL = src.substring(0, src.lastIndexOf('/'));
29224 				}
29225 			}
29226 
29227 			/**
29228 			 * Base URL where the root directory if TinyMCE is located.
29229 			 *
29230 			 * @property baseURL
29231 			 * @type String
29232 			 */
29233 			self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL);
29234 
29235 			/**
29236 			 * Document base URL where the current document is located.
29237 			 *
29238 			 * @property documentBaseURL
29239 			 * @type String
29240 			 */
29241 			self.documentBaseURL = documentBaseURL;
29242 
29243 			/**
29244 			 * Absolute baseURI for the installation path of TinyMCE.
29245 			 *
29246 			 * @property baseURI
29247 			 * @type tinymce.util.URI
29248 			 */
29249 			self.baseURI = new URI(self.baseURL);
29250 
29251 			/**
29252 			 * Current suffix to add to each plugin/theme that gets loaded for example ".min".
29253 			 *
29254 			 * @property suffix
29255 			 * @type String
29256 			 */
29257 			self.suffix = suffix;
29258 
29259 			self.focusManager = new FocusManager(self);
29260 		},
29261 
29262 		/**
29263 		 * Initializes a set of editors. This method will create editors based on various settings.
29264 		 *
29265 		 * @method init
29266 		 * @param {Object} settings Settings object to be passed to each editor instance.
29267 		 * @example
29268 		 * // Initializes a editor using the longer method
29269 		 * tinymce.EditorManager.init({
29270 		 *    some_settings : 'some value'
29271 		 * });
29272 		 *
29273 		 * // Initializes a editor instance using the shorter version
29274 		 * tinyMCE.init({
29275 		 *    some_settings : 'some value'
29276 		 * });
29277 		 */
29278 		init: function(settings) {
29279 			var self = this, editors = [], editor;
29280 
29281 			function createId(elm) {
29282 				var id = elm.id;
29283 
29284 				// Use element id, or unique name or generate a unique id
29285 				if (!id) {
29286 					id = elm.name;
29287 
29288 					if (id && !DOM.get(id)) {
29289 						id = elm.name;
29290 					} else {
29291 						// Generate unique name
29292 						id = DOM.uniqueId();
29293 					}
29294 
29295 					elm.setAttribute('id', id);
29296 				}
29297 
29298 				return id;
29299 			}
29300 
29301 			function createEditor(id, settings) {
29302 				if (!purgeDestroyedEditor(self.get(id))) {
29303 					var editor = new Editor(id, settings, self);
29304 					editors.push(editor);
29305 					editor.render();
29306 				}
29307 			}
29308 
29309 			function execCallback(se, n, s) {
29310 				var f = se[n];
29311 
29312 				if (!f) {
29313 					return;
29314 				}
29315 
29316 				return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
29317 			}
29318 
29319 			function hasClass(n, c) {
29320 				return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
29321 			}
29322 
29323 			function readyHandler() {
29324 				var l, co;
29325 
29326 				DOM.unbind(window, 'ready', readyHandler);
29327 
29328 				execCallback(settings, 'onpageload');
29329 
29330 				if (settings.types) {
29331 					// Process type specific selector
29332 					each(settings.types, function(type) {
29333 						each(DOM.select(type.selector), function(elm) {
29334 							createEditor(createId(elm), extend({}, settings, type));
29335 						});
29336 					});
29337 
29338 					return;
29339 				} else if (settings.selector) {
29340 					// Process global selector
29341 					each(DOM.select(settings.selector), function(elm) {
29342 						createEditor(createId(elm), settings);
29343 					});
29344 
29345 					return;
29346 				}
29347 
29348 				// Fallback to old setting
29349 				switch (settings.mode) {
29350 					case "exact":
29351 						l = settings.elements || '';
29352 
29353 						if (l.length > 0) {
29354 							each(explode(l), function(v) {
29355 								if (DOM.get(v)) {
29356 									editor = new Editor(v, settings, self);
29357 									editors.push(editor);
29358 									editor.render();
29359 								} else {
29360 									each(document.forms, function(f) {
29361 										each(f.elements, function(e) {
29362 											if (e.name === v) {
29363 												v = 'mce_editor_' + instanceCounter++;
29364 												DOM.setAttrib(e, 'id', v);
29365 												createEditor(v, settings);
29366 											}
29367 										});
29368 									});
29369 								}
29370 							});
29371 						}
29372 						break;
29373 
29374 					case "textareas":
29375 					case "specific_textareas":
29376 						each(DOM.select('textarea'), function(elm) {
29377 							if (settings.editor_deselector && hasClass(elm, settings.editor_deselector)) {
29378 								return;
29379 							}
29380 
29381 							if (!settings.editor_selector || hasClass(elm, settings.editor_selector)) {
29382 								createEditor(createId(elm), settings);
29383 							}
29384 						});
29385 						break;
29386 				}
29387 
29388 				// Call onInit when all editors are initialized
29389 				if (settings.oninit) {
29390 					l = co = 0;
29391 
29392 					each(editors, function(ed) {
29393 						co++;
29394 
29395 						if (!ed.initialized) {
29396 							// Wait for it
29397 							ed.on('init', function() {
29398 								l++;
29399 
29400 								// All done
29401 								if (l == co) {
29402 									execCallback(settings, 'oninit');
29403 								}
29404 							});
29405 						} else {
29406 							l++;
29407 						}
29408 
29409 						// All done
29410 						if (l == co) {
29411 							execCallback(settings, 'oninit');
29412 						}
29413 					});
29414 				}
29415 			}
29416 
29417 			self.settings = settings;
29418 
29419 			DOM.bind(window, 'ready', readyHandler);
29420 		},
29421 
29422 		/**
29423 		 * Returns a editor instance by id.
29424 		 *
29425 		 * @method get
29426 		 * @param {String/Number} id Editor instance id or index to return.
29427 		 * @return {tinymce.Editor} Editor instance to return.
29428 		 * @example
29429 		 * // Adds an onclick event to an editor by id (shorter version)
29430 		 * tinymce.get('mytextbox').on('click', function(e) {
29431 		 *    ed.windowManager.alert('Hello world!');
29432 		 * });
29433 		 *
29434 		 * // Adds an onclick event to an editor by id (longer version)
29435 		 * tinymce.EditorManager.get('mytextbox').on('click', function(e) {
29436 		 *    ed.windowManager.alert('Hello world!');
29437 		 * });
29438 		 */
29439 		get: function(id) {
29440 			if (!arguments.length) {
29441 				return this.editors;
29442 			}
29443 
29444 			return id in this.editors ? this.editors[id] : null;
29445 		},
29446 
29447 		/**
29448 		 * Adds an editor instance to the editor collection. This will also set it as the active editor.
29449 		 *
29450 		 * @method add
29451 		 * @param {tinymce.Editor} editor Editor instance to add to the collection.
29452 		 * @return {tinymce.Editor} The same instance that got passed in.
29453 		 */
29454 		add: function(editor) {
29455 			var self = this, editors = self.editors;
29456 
29457 			// Add named and index editor instance
29458 			editors[editor.id] = editor;
29459 			editors.push(editor);
29460 
29461 			self.activeEditor = editor;
29462 
29463 			/**
29464 			 * Fires when an editor is added to the EditorManager collection.
29465 			 *
29466 			 * @event AddEditor
29467 			 * @param {Object} e Event arguments.
29468 			 */
29469 			self.fire('AddEditor', {editor: editor});
29470 
29471 			if (!beforeUnloadDelegate) {
29472 				beforeUnloadDelegate = function() {
29473 					self.fire('BeforeUnload');
29474 				};
29475 
29476 				DOM.bind(window, 'beforeunload', beforeUnloadDelegate);
29477 			}
29478 
29479 			return editor;
29480 		},
29481 
29482 		/**
29483 		 * Creates an editor instance and adds it to the EditorManager collection.
29484 		 *
29485 		 * @method createEditor
29486 		 * @param {String} id Instance id to use for editor.
29487 		 * @param {Object} settings Editor instance settings.
29488 		 * @return {tinymce.Editor} Editor instance that got created.
29489 		 */
29490 		createEditor: function(id, settings) {
29491 			return this.add(new Editor(id, settings, this));
29492 		},
29493 
29494 		/**
29495 		 * Removes a editor or editors form page.
29496 		 *
29497 		 * @example
29498 		 * // Remove all editors bound to divs
29499 		 * tinymce.remove('div');
29500 		 *
29501 		 * // Remove all editors bound to textareas
29502 		 * tinymce.remove('textarea');
29503 		 *
29504 		 * // Remove all editors
29505 		 * tinymce.remove();
29506 		 *
29507 		 * // Remove specific instance by id
29508 		 * tinymce.remove('#id');
29509 		 *
29510 		 * @method remove
29511 		 * @param {tinymce.Editor/String/Object} [selector] CSS selector or editor instance to remove.
29512 		 * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null.
29513 		 */
29514 		remove: function(selector) {
29515 			var self = this, i, editors = self.editors, editor;
29516 
29517 			// Remove all editors
29518 			if (!selector) {
29519 				for (i = editors.length - 1; i >= 0; i--) {
29520 					self.remove(editors[i]);
29521 				}
29522 
29523 				return;
29524 			}
29525 
29526 			// Remove editors by selector
29527 			if (typeof(selector) == "string") {
29528 				selector = selector.selector || selector;
29529 
29530 				each(DOM.select(selector), function(elm) {
29531 					self.remove(editors[elm.id]);
29532 				});
29533 
29534 				return;
29535 			}
29536 
29537 			// Remove specific editor
29538 			editor = selector;
29539 
29540 			// Not in the collection
29541 			if (!editors[editor.id]) {
29542 				return null;
29543 			}
29544 
29545 			/**
29546 			 * Fires when an editor is removed from EditorManager collection.
29547 			 *
29548 			 * @event RemoveEditor
29549 			 * @param {Object} e Event arguments.
29550 			 */
29551 			if (removeEditorFromList(editor)) {
29552 				self.fire('RemoveEditor', {editor: editor});
29553 			}
29554 
29555 			if (!editors.length) {
29556 				DOM.unbind(window, 'beforeunload', beforeUnloadDelegate);
29557 			}
29558 
29559 			editor.remove();
29560 
29561 			return editor;
29562 		},
29563 
29564 		/**
29565 		 * Executes a specific command on the currently active editor.
29566 		 *
29567 		 * @method execCommand
29568 		 * @param {String} c Command to perform for example Bold.
29569 		 * @param {Boolean} u Optional boolean state if a UI should be presented for the command or not.
29570 		 * @param {String} v Optional value parameter like for example an URL to a link.
29571 		 * @return {Boolean} true/false if the command was executed or not.
29572 		 */
29573 		execCommand: function(cmd, ui, value) {
29574 			var self = this, editor = self.get(value);
29575 
29576 			// Manager commands
29577 			switch (cmd) {
29578 				case "mceAddEditor":
29579 					if (!self.get(value)) {
29580 						new Editor(value, self.settings, self).render();
29581 					}
29582 
29583 					return true;
29584 
29585 				case "mceRemoveEditor":
29586 					if (editor) {
29587 						editor.remove();
29588 					}
29589 
29590 					return true;
29591 
29592 				case 'mceToggleEditor':
29593 					if (!editor) {
29594 						self.execCommand('mceAddEditor', 0, value);
29595 						return true;
29596 					}
29597 
29598 					if (editor.isHidden()) {
29599 						editor.show();
29600 					} else {
29601 						editor.hide();
29602 					}
29603 
29604 					return true;
29605 			}
29606 
29607 			// Run command on active editor
29608 			if (self.activeEditor) {
29609 				return self.activeEditor.execCommand(cmd, ui, value);
29610 			}
29611 
29612 			return false;
29613 		},
29614 
29615 		/**
29616 		 * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted.
29617 		 *
29618 		 * @method triggerSave
29619 		 * @example
29620 		 * // Saves all contents
29621 		 * tinyMCE.triggerSave();
29622 		 */
29623 		triggerSave: function() {
29624 			each(this.editors, function(editor) {
29625 				editor.save();
29626 			});
29627 		},
29628 
29629 		/**
29630 		 * Adds a language pack, this gets called by the loaded language files like en.js.
29631 		 *
29632 		 * @method addI18n
29633 		 * @param {String} code Optional language code.
29634 		 * @param {Object} items Name/value object with translations.
29635 		 */
29636 		addI18n: function(code, items) {
29637 			I18n.add(code, items);
29638 		},
29639 
29640 		/**
29641 		 * Translates the specified string using the language pack items.
29642 		 *
29643 		 * @method translate
29644 		 * @param {String/Array/Object} text String to translate
29645 		 * @return {String} Translated string.
29646 		 */
29647 		translate: function(text) {
29648 			return I18n.translate(text);
29649 		}
29650 	};
29651 
29652 	extend(EditorManager, Observable);
29653 
29654 	EditorManager.setup();
29655 
29656 	// Export EditorManager as tinymce/tinymce in global namespace
29657 	window.tinymce = window.tinyMCE = EditorManager;
29658 
29659 	return EditorManager;
29660 });
29661 
29662 // Included from: js/tinymce/classes/LegacyInput.js
29663 
29664 /**
29665  * LegacyInput.js
29666  *
29667  * Copyright, Moxiecode Systems AB
29668  * Released under LGPL License.
29669  *
29670  * License: http://www.tinymce.com/license
29671  * Contributing: http://www.tinymce.com/contributing
29672  */
29673 
29674 define("tinymce/LegacyInput", [
29675 	"tinymce/EditorManager",
29676 	"tinymce/util/Tools"
29677 ], function(EditorManager, Tools) {
29678 	var each = Tools.each, explode = Tools.explode;
29679 
29680 	EditorManager.on('AddEditor', function(e) {
29681 		var editor = e.editor;
29682 
29683 		editor.on('preInit', function() {
29684 			var filters, fontSizes, dom, settings = editor.settings;
29685 
29686 			function replaceWithSpan(node, styles) {
29687 				each(styles, function(value, name) {
29688 					if (value) {
29689 						dom.setStyle(node, name, value);
29690 					}
29691 				});
29692 
29693 				dom.rename(node, 'span');
29694 			}
29695 
29696 			function convert(e) {
29697 				dom = editor.dom;
29698 
29699 				if (settings.convert_fonts_to_spans) {
29700 					each(dom.select('font,u,strike', e.node), function(node) {
29701 						filters[node.nodeName.toLowerCase()](dom, node);
29702 					});
29703 				}
29704 			}
29705 
29706 			if (settings.inline_styles) {
29707 				fontSizes = explode(settings.font_size_legacy_values);
29708 
29709 				filters = {
29710 					font: function(dom, node) {
29711 						replaceWithSpan(node, {
29712 							backgroundColor: node.style.backgroundColor,
29713 							color: node.color,
29714 							fontFamily: node.face,
29715 							fontSize: fontSizes[parseInt(node.size, 10) - 1]
29716 						});
29717 					},
29718 
29719 					u: function(dom, node) {
29720 						replaceWithSpan(node, {
29721 							textDecoration: 'underline'
29722 						});
29723 					},
29724 
29725 					strike: function(dom, node) {
29726 						replaceWithSpan(node, {
29727 							textDecoration: 'line-through'
29728 						});
29729 					}
29730 				};
29731 
29732 				editor.on('PreProcess SetContent', convert);
29733 			}
29734 		});
29735 	});
29736 });
29737 
29738 // Included from: js/tinymce/classes/util/XHR.js
29739 
29740 /**
29741  * XHR.js
29742  *
29743  * Copyright, Moxiecode Systems AB
29744  * Released under LGPL License.
29745  *
29746  * License: http://www.tinymce.com/license
29747  * Contributing: http://www.tinymce.com/contributing
29748  */
29749 
29750 /**
29751  * This class enables you to send XMLHTTPRequests cross browser.
29752  * @class tinymce.util.XHR
29753  * @static
29754  * @example
29755  * // Sends a low level Ajax request
29756  * tinymce.util.XHR.send({
29757  *    url: 'someurl',
29758  *    success: function(text) {
29759  *       console.debug(text);
29760  *    }
29761  * });
29762  */
29763 define("tinymce/util/XHR", [], function() {
29764 	return {
29765 		/**
29766 		 * Sends a XMLHTTPRequest.
29767 		 * Consult the Wiki for details on what settings this method takes.
29768 		 *
29769 		 * @method send
29770 		 * @param {Object} settings Object will target URL, callbacks and other info needed to make the request.
29771 		 */
29772 		send: function(settings) {
29773 			var xhr, count = 0;
29774 
29775 			function ready() {
29776 				if (!settings.async || xhr.readyState == 4 || count++ > 10000) {
29777 					if (settings.success && count < 10000 && xhr.status == 200) {
29778 						settings.success.call(settings.success_scope, '' + xhr.responseText, xhr, settings);
29779 					} else if (settings.error) {
29780 						settings.error.call(settings.error_scope, count > 10000 ? 'TIMED_OUT' : 'GENERAL', xhr, settings);
29781 					}
29782 
29783 					xhr = null;
29784 				} else {
29785 					setTimeout(ready, 10);
29786 				}
29787 			}
29788 
29789 			// Default settings
29790 			settings.scope = settings.scope || this;
29791 			settings.success_scope = settings.success_scope || settings.scope;
29792 			settings.error_scope = settings.error_scope || settings.scope;
29793 			settings.async = settings.async === false ? false : true;
29794 			settings.data = settings.data || '';
29795 
29796 			xhr = new XMLHttpRequest();
29797 
29798 			if (xhr) {
29799 				if (xhr.overrideMimeType) {
29800 					xhr.overrideMimeType(settings.content_type);
29801 				}
29802 
29803 				xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async);
29804 
29805 				if (settings.content_type) {
29806 					xhr.setRequestHeader('Content-Type', settings.content_type);
29807 				}
29808 
29809 				xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
29810 
29811 				xhr.send(settings.data);
29812 
29813 				// Syncronous request
29814 				if (!settings.async) {
29815 					return ready();
29816 				}
29817 
29818 				// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
29819 				setTimeout(ready, 10);
29820 			}
29821 		}
29822 	};
29823 });
29824 
29825 // Included from: js/tinymce/classes/util/JSON.js
29826 
29827 /**
29828  * JSON.js
29829  *
29830  * Copyright, Moxiecode Systems AB
29831  * Released under LGPL License.
29832  *
29833  * License: http://www.tinymce.com/license
29834  * Contributing: http://www.tinymce.com/contributing
29835  */
29836 
29837 /**
29838  * JSON parser and serializer class.
29839  *
29840  * @class tinymce.util.JSON
29841  * @static
29842  * @example
29843  * // JSON parse a string into an object
29844  * var obj = tinymce.util.JSON.parse(somestring);
29845  *
29846  * // JSON serialize a object into an string
29847  * var str = tinymce.util.JSON.serialize(obj);
29848  */
29849 define("tinymce/util/JSON", [], function() {
29850 	function serialize(o, quote) {
29851 		var i, v, t, name;
29852 
29853 		quote = quote || '"';
29854 
29855 		if (o === null) {
29856 			return 'null';
29857 		}
29858 
29859 		t = typeof o;
29860 
29861 		if (t == 'string') {
29862 			v = '\bb\tt\nn\ff\rr\""\'\'\\\\';
29863 
29864 			return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) {
29865 				// Make sure single quotes never get encoded inside double quotes for JSON compatibility
29866 				if (quote === '"' && a === "'") {
29867 					return a;
29868 				}
29869 
29870 				i = v.indexOf(b);
29871 
29872 				if (i + 1) {
29873 					return '\\' + v.charAt(i + 1);
29874 				}
29875 
29876 				a = b.charCodeAt().toString(16);
29877 
29878 				return '\\u' + '0000'.substring(a.length) + a;
29879 			}) + quote;
29880 		}
29881 
29882 		if (t == 'object') {
29883 			if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') {
29884 					for (i = 0, v = '['; i < o.length; i++) {
29885 						v += (i > 0 ? ',' : '') + serialize(o[i], quote);
29886 					}
29887 
29888 					return v + ']';
29889 				}
29890 
29891 				v = '{';
29892 
29893 				for (name in o) {
29894 					if (o.hasOwnProperty(name)) {
29895 						v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name +
29896 							quote + ':' + serialize(o[name], quote) : '';
29897 					}
29898 				}
29899 
29900 				return v + '}';
29901 		}
29902 
29903 		return '' + o;
29904 	}
29905 
29906 	return {
29907 		/**
29908 		 * Serializes the specified object as a JSON string.
29909 		 *
29910 		 * @method serialize
29911 		 * @param {Object} obj Object to serialize as a JSON string.
29912 		 * @param {String} quote Optional quote string defaults to ".
29913 		 * @return {string} JSON string serialized from input.
29914 		 */
29915 		serialize: serialize,
29916 
29917 		/**
29918 		 * Unserializes/parses the specified JSON string into a object.
29919 		 *
29920 		 * @method parse
29921 		 * @param {string} s JSON String to parse into a JavaScript object.
29922 		 * @return {Object} Object from input JSON string or undefined if it failed.
29923 		 */
29924 		parse: function(text) {
29925 			try {
29926 				// Trick uglify JS
29927 				return window[String.fromCharCode(101) + 'val']('(' + text + ')');
29928 			} catch (ex) {
29929 				// Ignore
29930 			}
29931 		}
29932 
29933 		/**#@-*/
29934 	};
29935 });
29936 
29937 // Included from: js/tinymce/classes/util/JSONRequest.js
29938 
29939 /**
29940  * JSONRequest.js
29941  *
29942  * Copyright, Moxiecode Systems AB
29943  * Released under LGPL License.
29944  *
29945  * License: http://www.tinymce.com/license
29946  * Contributing: http://www.tinymce.com/contributing
29947  */
29948 
29949 /**
29950  * This class enables you to use JSON-RPC to call backend methods.
29951  *
29952  * @class tinymce.util.JSONRequest
29953  * @example
29954  * var json = new tinymce.util.JSONRequest({
29955  *     url: 'somebackend.php'
29956  * });
29957  *
29958  * // Send RPC call 1
29959  * json.send({
29960  *     method: 'someMethod1',
29961  *     params: ['a', 'b'],
29962  *     success: function(result) {
29963  *         console.dir(result);
29964  *     }
29965  * });
29966  *
29967  * // Send RPC call 2
29968  * json.send({
29969  *     method: 'someMethod2',
29970  *     params: ['a', 'b'],
29971  *     success: function(result) {
29972  *         console.dir(result);
29973  *     }
29974  * });
29975  */
29976 define("tinymce/util/JSONRequest", [
29977 	"tinymce/util/JSON",
29978 	"tinymce/util/XHR",
29979 	"tinymce/util/Tools"
29980 ], function(JSON, XHR, Tools) {
29981 	var extend = Tools.extend;
29982 
29983 	function JSONRequest(settings) {
29984 		this.settings = extend({}, settings);
29985 		this.count = 0;
29986 	}
29987 
29988 	/**
29989 	 * Simple helper function to send a JSON-RPC request without the need to initialize an object.
29990 	 * Consult the Wiki API documentation for more details on what you can pass to this function.
29991 	 *
29992 	 * @method sendRPC
29993 	 * @static
29994 	 * @param {Object} o Call object where there are three field id, method and params this object should also contain callbacks etc.
29995 	 */
29996 	JSONRequest.sendRPC = function(o) {
29997 		return new JSONRequest().send(o);
29998 	};
29999 
30000 	JSONRequest.prototype = {
30001 		/**
30002 		 * Sends a JSON-RPC call. Consult the Wiki API documentation for more details on what you can pass to this function.
30003 		 *
30004 		 * @method send
30005 		 * @param {Object} args Call object where there are three field id, method and params this object should also contain callbacks etc.
30006 		 */
30007 		send: function(args) {
30008 			var ecb = args.error, scb = args.success;
30009 
30010 			args = extend(this.settings, args);
30011 
30012 			args.success = function(c, x) {
30013 				c = JSON.parse(c);
30014 
30015 				if (typeof(c) == 'undefined') {
30016 					c = {
30017 						error : 'JSON Parse error.'
30018 					};
30019 				}
30020 
30021 				if (c.error) {
30022 					ecb.call(args.error_scope || args.scope, c.error, x);
30023 				} else {
30024 					scb.call(args.success_scope || args.scope, c.result);
30025 				}
30026 			};
30027 
30028 			args.error = function(ty, x) {
30029 				if (ecb) {
30030 					ecb.call(args.error_scope || args.scope, ty, x);
30031 				}
30032 			};
30033 
30034 			args.data = JSON.serialize({
30035 				id: args.id || 'c' + (this.count++),
30036 				method: args.method,
30037 				params: args.params
30038 			});
30039 
30040 			// JSON content type for Ruby on rails. Bug: #1883287
30041 			args.content_type = 'application/json';
30042 
30043 			XHR.send(args);
30044 		}
30045 	};
30046 
30047 	return JSONRequest;
30048 });
30049 
30050 // Included from: js/tinymce/classes/util/JSONP.js
30051 
30052 /**
30053  * JSONP.js
30054  *
30055  * Copyright, Moxiecode Systems AB
30056  * Released under LGPL License.
30057  *
30058  * License: http://www.tinymce.com/license
30059  * Contributing: http://www.tinymce.com/contributing
30060  */
30061 
30062 define("tinymce/util/JSONP", [
30063 	"tinymce/dom/DOMUtils"
30064 ], function(DOMUtils) {
30065 	return {
30066 		callbacks: {},
30067 		count: 0,
30068 
30069 		send: function(settings) {
30070 			var self = this, dom = DOMUtils.DOM, count = settings.count !== undefined ? settings.count : self.count;
30071 			var id = 'tinymce_jsonp_' + count;
30072 
30073 			self.callbacks[count] = function(json) {
30074 				dom.remove(id);
30075 				delete self.callbacks[count];
30076 
30077 				settings.callback(json);
30078 			};
30079 
30080 			dom.add(dom.doc.body, 'script', {
30081 				id: id,
30082 				src: settings.url,
30083 				type: 'text/javascript'
30084 			});
30085 
30086 			self.count++;
30087 		}
30088 	};
30089 });
30090 
30091 // Included from: js/tinymce/classes/util/LocalStorage.js
30092 
30093 /**
30094  * LocalStorage.js
30095  *
30096  * Copyright, Moxiecode Systems AB
30097  * Released under LGPL License.
30098  *
30099  * License: http://www.tinymce.com/license
30100  * Contributing: http://www.tinymce.com/contributing
30101  */
30102 
30103 /**
30104  * This class will simulate LocalStorage on IE 7 and return the native version on modern browsers.
30105  * Storage is done using userData on IE 7 and a special serialization format. The format is designed
30106  * to be as small as possible by making sure that the keys and values doesn't need to be encoded. This
30107  * makes it possible to store for example HTML data.
30108  *
30109  * Storage format for userData:
30110  * <base 32 key length>,<key string>,<base 32 value length>,<value>,...
30111  *
30112  * For example this data key1=value1,key2=value2 would be:
30113  * 4,key1,6,value1,4,key2,6,value2
30114  *
30115  * @class tinymce.util.LocalStorage
30116  * @static
30117  * @version 4.0
30118  * @example
30119  * tinymce.util.LocalStorage.setItem('key', 'value');
30120  * var value = tinymce.util.LocalStorage.getItem('key');
30121  */
30122 define("tinymce/util/LocalStorage", [], function() {
30123 	var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport;
30124 
30125 	// Check for native support
30126 	try {
30127 		if (window.localStorage) {
30128 			return localStorage;
30129 		}
30130 	} catch (ex) {
30131 		// Ignore
30132 	}
30133 
30134 	userDataKey = "tinymce";
30135 	storageElm = document.documentElement;
30136 	hasOldIEDataSupport = !!storageElm.addBehavior;
30137 
30138 	if (hasOldIEDataSupport) {
30139 		storageElm.addBehavior('#default#userData');
30140 	}
30141 
30142 	/**
30143 	 * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters.
30144 	 */
30145 	function updateKeys() {
30146 		keys = [];
30147 
30148 		for (var key in items) {
30149 			keys.push(key);
30150 		}
30151 
30152 		LocalStorage.length = keys.length;
30153 	}
30154 
30155 	/**
30156 	 * Loads the userData string and parses it into the items structure.
30157 	 */
30158 	function load() {
30159 		var key, data, value, pos = 0;
30160 
30161 		items = {};
30162 
30163 		// localStorage can be disabled on WebKit/Gecko so make a dummy storage
30164 		if (!hasOldIEDataSupport) {
30165 			return;
30166 		}
30167 
30168 		function next(end) {
30169 			var value, nextPos;
30170 
30171 			nextPos = end !== undefined ? pos + end : data.indexOf(',', pos);
30172 			if (nextPos === -1 || nextPos > data.length) {
30173 				return null;
30174 			}
30175 
30176 			value = data.substring(pos, nextPos);
30177 			pos = nextPos + 1;
30178 
30179 			return value;
30180 		}
30181 
30182 		storageElm.load(userDataKey);
30183 		data = storageElm.getAttribute(userDataKey) || '';
30184 
30185 		do {
30186 			var offset = next();
30187 			if (offset === null) {
30188 				break;
30189 			}
30190 
30191 			key = next(parseInt(offset, 32) || 0);
30192 			if (key !== null) {
30193 				offset = next();
30194 				if (offset === null) {
30195 					break;
30196 				}
30197 
30198 				value = next(parseInt(offset, 32) || 0);
30199 
30200 				if (key) {
30201 					items[key] = value;
30202 				}
30203 			}
30204 		} while (key !== null);
30205 
30206 		updateKeys();
30207 	}
30208 
30209 	/**
30210 	 * Saves the items structure into a the userData format.
30211 	 */
30212 	function save() {
30213 		var value, data = '';
30214 
30215 		// localStorage can be disabled on WebKit/Gecko so make a dummy storage
30216 		if (!hasOldIEDataSupport) {
30217 			return;
30218 		}
30219 
30220 		for (var key in items) {
30221 			value = items[key];
30222 			data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value;
30223 		}
30224 
30225 		storageElm.setAttribute(userDataKey, data);
30226 
30227 		try {
30228 			storageElm.save(userDataKey);
30229 		} catch (ex) {
30230 			// Ignore disk full
30231 		}
30232 
30233 		updateKeys();
30234 	}
30235 
30236 	LocalStorage = {
30237 		/**
30238 		 * Length of the number of items in storage.
30239 		 *
30240 		 * @property length
30241 		 * @type Number
30242 		 * @return {Number} Number of items in storage.
30243 		 */
30244 		//length:0,
30245 
30246 		/**
30247 		 * Returns the key name by index.
30248 		 *
30249 		 * @method key
30250 		 * @param {Number} index Index of key to return.
30251 		 * @return {String} Key value or null if it wasn't found.
30252 		 */
30253 		key: function(index) {
30254 			return keys[index];
30255 		},
30256 
30257 		/**
30258 		 * Returns the value if the specified key or null if it wasn't found.
30259 		 *
30260 		 * @method getItem
30261 		 * @param {String} key Key of item to retrive.
30262 		 * @return {String} Value of the specified item or null if it wasn't found.
30263 		 */
30264 		getItem: function(key) {
30265 			return key in items ? items[key] : null;
30266 		},
30267 
30268 		/**
30269 		 * Sets the value of the specified item by it's key.
30270 		 *
30271 		 * @method setItem
30272 		 * @param {String} key Key of the item to set.
30273 		 * @param {String} value Value of the item to set.
30274 		 */
30275 		setItem: function(key, value) {
30276 			items[key] = "" + value;
30277 			save();
30278 		},
30279 
30280 		/**
30281 		 * Removes the specified item by key.
30282 		 *
30283 		 * @method removeItem
30284 		 * @param {String} key Key of item to remove.
30285 		 */
30286 		removeItem: function(key) {
30287 			delete items[key];
30288 			save();
30289 		},
30290 
30291 		/**
30292 		 * Removes all items.
30293 		 *
30294 		 * @method clear
30295 		 */
30296 		clear: function() {
30297 			items = {};
30298 			save();
30299 		}
30300 	};
30301 
30302 	load();
30303 
30304 	return LocalStorage;
30305 });
30306 
30307 // Included from: js/tinymce/classes/Compat.js
30308 
30309 /**
30310  * Compat.js
30311  *
30312  * Copyright, Moxiecode Systems AB
30313  * Released under LGPL License.
30314  *
30315  * License: http://www.tinymce.com/license
30316  * Contributing: http://www.tinymce.com/contributing
30317  */
30318 
30319 /**
30320  * TinyMCE core class.
30321  *
30322  * @static
30323  * @class tinymce
30324  * @borrow-members tinymce.EditorManager
30325  * @borrow-members tinymce.util.Tools
30326  */
30327 define("tinymce/Compat", [
30328 	"tinymce/dom/DOMUtils",
30329 	"tinymce/dom/EventUtils",
30330 	"tinymce/dom/ScriptLoader",
30331 	"tinymce/AddOnManager",
30332 	"tinymce/util/Tools",
30333 	"tinymce/Env"
30334 ], function(DOMUtils, EventUtils, ScriptLoader, AddOnManager, Tools, Env) {
30335 	var tinymce = window.tinymce;
30336 
30337 	/**
30338 	 * @property {tinymce.dom.DOMUtils} DOM Global DOM instance.
30339 	 * @property {tinymce.dom.ScriptLoader} ScriptLoader Global ScriptLoader instance.
30340 	 * @property {tinymce.AddOnManager} PluginManager Global PluginManager instance.
30341 	 * @property {tinymce.AddOnManager} ThemeManager Global ThemeManager instance.
30342 	 */
30343 	tinymce.DOM = DOMUtils.DOM;
30344 	tinymce.ScriptLoader = ScriptLoader.ScriptLoader;
30345 	tinymce.PluginManager = AddOnManager.PluginManager;
30346 	tinymce.ThemeManager = AddOnManager.ThemeManager;
30347 
30348 	tinymce.dom = tinymce.dom || {};
30349 	tinymce.dom.Event = EventUtils.Event;
30350 
30351 	Tools.each(Tools, function(func, key) {
30352 		tinymce[key] = func;
30353 	});
30354 
30355 	Tools.each('isOpera isWebKit isIE isGecko isMac'.split(' '), function(name) {
30356 		tinymce[name] = Env[name.substr(2).toLowerCase()];
30357 	});
30358 
30359 	return {};
30360 });
30361 
30362 // Describe the different namespaces
30363 
30364 /**
30365  * Root level namespace this contains classes directly releated to the TinyMCE editor.
30366  *
30367  * @namespace tinymce
30368  */
30369 
30370 /**
30371  * Contains classes for handling the browsers DOM.
30372  *
30373  * @namespace tinymce.dom
30374  */
30375 
30376 /**
30377  * Contains html parser and serializer logic.
30378  *
30379  * @namespace tinymce.html
30380  */
30381 
30382 /**
30383  * Contains the different UI types such as buttons, listboxes etc.
30384  *
30385  * @namespace tinymce.ui
30386  */
30387 
30388 /**
30389  * Contains various utility classes such as json parser, cookies etc.
30390  *
30391  * @namespace tinymce.util
30392  */
30393 
30394 // Included from: js/tinymce/classes/ui/Layout.js
30395 
30396 /**
30397  * Layout.js
30398  *
30399  * Copyright, Moxiecode Systems AB
30400  * Released under LGPL License.
30401  *
30402  * License: http://www.tinymce.com/license
30403  * Contributing: http://www.tinymce.com/contributing
30404  */
30405 
30406 /**
30407  * Base layout manager class.
30408  *
30409  * @class tinymce.ui.Layout
30410  */
30411 define("tinymce/ui/Layout", [
30412 	"tinymce/util/Class",
30413 	"tinymce/util/Tools"
30414 ], function(Class, Tools) {
30415 	"use strict";
30416 
30417 	return Class.extend({
30418 		Defaults: {
30419 			firstControlClass: 'first',
30420 			lastControlClass: 'last'
30421 		},
30422 
30423 		/**
30424 		 * Constructs a layout instance with the specified settings.
30425 		 *
30426 		 * @constructor
30427 		 * @param {Object} settings Name/value object with settings.
30428 		 */
30429 		init: function(settings) {
30430 			this.settings = Tools.extend({}, this.Defaults, settings);
30431 		},
30432 
30433 		/**
30434 		 * This method gets invoked before the layout renders the controls.
30435 		 *
30436 		 * @method preRender
30437 		 * @param {tinymce.ui.Container} container Container instance to preRender.
30438 		 */
30439 		preRender: function(container) {
30440 			container.addClass(this.settings.containerClass, 'body');
30441 		},
30442 
30443 		/**
30444 		 * Applies layout classes to the container.
30445 		 *
30446 		 * @private
30447 		 */
30448 		applyClasses: function(container) {
30449 			var self = this, settings = self.settings, items, firstClass, lastClass;
30450 
30451 			items = container.items().filter(':visible');
30452 			firstClass = settings.firstControlClass;
30453 			lastClass = settings.lastControlClass;
30454 
30455 			items.each(function(item) {
30456 				item.removeClass(firstClass).removeClass(lastClass);
30457 
30458 				if (settings.controlClass) {
30459 					item.addClass(settings.controlClass);
30460 				}
30461 			});
30462 
30463 			items.eq(0).addClass(firstClass);
30464 			items.eq(-1).addClass(lastClass);
30465 		},
30466 
30467 		/**
30468 		 * Renders the specified container and any layout specific HTML.
30469 		 *
30470 		 * @method renderHtml
30471 		 * @param {tinymce.ui.Container} container Container to render HTML for.
30472 		 */
30473 		renderHtml: function(container) {
30474 			var self = this, settings = self.settings, items, html = '';
30475 
30476 			items = container.items();
30477 			items.eq(0).addClass(settings.firstControlClass);
30478 			items.eq(-1).addClass(settings.lastControlClass);
30479 
30480 			items.each(function(item) {
30481 				if (settings.controlClass) {
30482 					item.addClass(settings.controlClass);
30483 				}
30484 
30485 				html += item.renderHtml();
30486 			});
30487 
30488 			return html;
30489 		},
30490 
30491 		/**
30492 		 * Recalculates the positions of the controls in the specified container.
30493 		 *
30494 		 * @method recalc
30495 		 * @param {tinymce.ui.Container} container Container instance to recalc.
30496 		 */
30497 		recalc: function() {
30498 		},
30499 
30500 		/**
30501 		 * This method gets invoked after the layout renders the controls.
30502 		 *
30503 		 * @method postRender
30504 		 * @param {tinymce.ui.Container} container Container instance to postRender.
30505 		 */
30506 		postRender: function() {
30507 		}
30508 	});
30509 });
30510 
30511 // Included from: js/tinymce/classes/ui/AbsoluteLayout.js
30512 
30513 /**
30514  * AbsoluteLayout.js
30515  *
30516  * Copyright, Moxiecode Systems AB
30517  * Released under LGPL License.
30518  *
30519  * License: http://www.tinymce.com/license
30520  * Contributing: http://www.tinymce.com/contributing
30521  */
30522 
30523 /**
30524  * LayoutManager for absolute positioning. This layout manager is more of
30525  * a base class for other layouts but can be created and used directly.
30526  *
30527  * @-x-less AbsoluteLayout.less
30528  * @class tinymce.ui.AbsoluteLayout
30529  * @extends tinymce.ui.Layout
30530  */
30531 define("tinymce/ui/AbsoluteLayout", [
30532 	"tinymce/ui/Layout"
30533 ], function(Layout) {
30534 	"use strict";
30535 
30536 	return Layout.extend({
30537 		Defaults: {
30538 			containerClass: 'abs-layout',
30539 			controlClass: 'abs-layout-item'
30540 		},
30541 
30542 		/**
30543 		 * Recalculates the positions of the controls in the specified container.
30544 		 *
30545 		 * @method recalc
30546 		 * @param {tinymce.ui.Container} container Container instance to recalc.
30547 		 */
30548 		recalc: function(container) {
30549 			container.items().filter(':visible').each(function(ctrl) {
30550 				var settings = ctrl.settings;
30551 
30552 				ctrl.layoutRect({
30553 					x: settings.x,
30554 					y: settings.y,
30555 					w: settings.w,
30556 					h: settings.h
30557 				});
30558 
30559 				if (ctrl.recalc) {
30560 					ctrl.recalc();
30561 				}
30562 			});
30563 		},
30564 
30565 		/**
30566 		 * Renders the specified container and any layout specific HTML.
30567 		 *
30568 		 * @method renderHtml
30569 		 * @param {tinymce.ui.Container} container Container to render HTML for.
30570 		 */
30571 		renderHtml: function(container) {
30572 			return '<div id="' + container._id + '-absend" class="' + container.classPrefix + 'abs-end"></div>' + this._super(container);
30573 		}
30574 	});
30575 });
30576 
30577 // Included from: js/tinymce/classes/ui/Tooltip.js
30578 
30579 /**
30580  * Tooltip.js
30581  *
30582  * Copyright, Moxiecode Systems AB
30583  * Released under LGPL License.
30584  *
30585  * License: http://www.tinymce.com/license
30586  * Contributing: http://www.tinymce.com/contributing
30587  */
30588 
30589 /**
30590  * Creates a tooltip instance.
30591  *
30592  * @-x-less ToolTip.less
30593  * @class tinymce.ui.ToolTip
30594  * @extends tinymce.ui.Control
30595  * @mixes tinymce.ui.Movable
30596  */
30597 define("tinymce/ui/Tooltip", [
30598 	"tinymce/ui/Control",
30599 	"tinymce/ui/Movable"
30600 ], function(Control, Movable) {
30601 	return Control.extend({
30602 		Mixins: [Movable],
30603 
30604 		Defaults: {
30605 			classes: 'widget tooltip tooltip-n'
30606 		},
30607 
30608 		/**
30609 		 * Sets/gets the current label text.
30610 		 *
30611 		 * @method text
30612 		 * @param {String} [text] New label text.
30613 		 * @return {String|tinymce.ui.Tooltip} Current text or current label instance.
30614 		 */
30615 		text: function(value) {
30616 			var self = this;
30617 
30618 			if (typeof(value) != "undefined") {
30619 				self._value = value;
30620 
30621 				if (self._rendered) {
30622 					self.getEl().lastChild.innerHTML = self.encode(value);
30623 				}
30624 
30625 				return self;
30626 			}
30627 
30628 			return self._value;
30629 		},
30630 
30631 		/**
30632 		 * Renders the control as a HTML string.
30633 		 *
30634 		 * @method renderHtml
30635 		 * @return {String} HTML representing the control.
30636 		 */
30637 		renderHtml: function() {
30638 			var self = this, prefix = self.classPrefix;
30639 
30640 			return (
30641 				'<div id="' + self._id + '" class="' + self.classes() + '" role="presentation">' +
30642 					'<div class="' + prefix + 'tooltip-arrow"></div>' +
30643 					'<div class="' + prefix + 'tooltip-inner">' + self.encode(self._text) + '</div>' +
30644 				'</div>'
30645 			);
30646 		},
30647 
30648 		/**
30649 		 * Repaints the control after a layout operation.
30650 		 *
30651 		 * @method repaint
30652 		 */
30653 		repaint: function() {
30654 			var self = this, style, rect;
30655 
30656 			style = self.getEl().style;
30657 			rect = self._layoutRect;
30658 
30659 			style.left = rect.x + 'px';
30660 			style.top = rect.y + 'px';
30661 			style.zIndex = 0xFFFF + 0xFFFF;
30662 		}
30663 	});
30664 });
30665 
30666 // Included from: js/tinymce/classes/ui/Widget.js
30667 
30668 /**
30669  * Widget.js
30670  *
30671  * Copyright, Moxiecode Systems AB
30672  * Released under LGPL License.
30673  *
30674  * License: http://www.tinymce.com/license
30675  * Contributing: http://www.tinymce.com/contributing
30676  */
30677 
30678 /**
30679  * Widget base class a widget is a control that has a tooltip and some basic states.
30680  *
30681  * @class tinymce.ui.Widget
30682  * @extends tinymce.ui.Control
30683  */
30684 define("tinymce/ui/Widget", [
30685 	"tinymce/ui/Control",
30686 	"tinymce/ui/Tooltip"
30687 ], function(Control, Tooltip) {
30688 	"use strict";
30689 
30690 	var tooltip;
30691 
30692 	var Widget = Control.extend({
30693 		/**
30694 		 * Constructs a instance with the specified settings.
30695 		 *
30696 		 * @constructor
30697 		 * @param {Object} settings Name/value object with settings.
30698 		 * @setting {String} tooltip Tooltip text to display when hovering.
30699 		 * @setting {Boolean} autofocus True if the control should be focused when rendered.
30700 		 * @setting {String} text Text to display inside widget.
30701 		 */
30702 		init: function(settings) {
30703 			var self = this;
30704 
30705 			self._super(settings);
30706 			settings = self.settings;
30707 			self.canFocus = true;
30708 
30709 			if (settings.tooltip && Widget.tooltips !== false) {
30710 				self.on('mouseenter', function(e) {
30711 					var tooltip = self.tooltip().moveTo(-0xFFFF);
30712 
30713 					if (e.control == self) {
30714 						var rel = tooltip.text(settings.tooltip).show().testMoveRel(self.getEl(), ['bc-tc', 'bc-tl', 'bc-tr']);
30715 
30716 						tooltip.toggleClass('tooltip-n', rel == 'bc-tc');
30717 						tooltip.toggleClass('tooltip-nw', rel == 'bc-tl');
30718 						tooltip.toggleClass('tooltip-ne', rel == 'bc-tr');
30719 
30720 						tooltip.moveRel(self.getEl(), rel);
30721 					} else {
30722 						tooltip.hide();
30723 					}
30724 				});
30725 
30726 				self.on('mouseleave mousedown click', function() {
30727 					self.tooltip().hide();
30728 				});
30729 			}
30730 
30731 			self.aria('label', settings.ariaLabel || settings.tooltip);
30732 		},
30733 
30734 		/**
30735 		 * Returns the current tooltip instance.
30736 		 *
30737 		 * @method tooltip
30738 		 * @return {tinymce.ui.Tooltip} Tooltip instance.
30739 		 */
30740 		tooltip: function() {
30741 			if (!tooltip) {
30742 				tooltip = new Tooltip({type: 'tooltip'});
30743 				tooltip.renderTo();
30744 			}
30745 
30746 			return tooltip;
30747 		},
30748 
30749 		/**
30750 		 * Sets/gets the active state of the widget.
30751 		 *
30752 		 * @method active
30753 		 * @param {Boolean} [state] State if the control is active.
30754 		 * @return {Boolean|tinymce.ui.Widget} True/false or current widget instance.
30755 		 */
30756 		active: function(state) {
30757 			var self = this, undef;
30758 
30759 			if (state !== undef) {
30760 				self.aria('pressed', state);
30761 				self.toggleClass('active', state);
30762 			}
30763 
30764 			return self._super(state);
30765 		},
30766 
30767 		/**
30768 		 * Sets/gets the disabled state of the widget.
30769 		 *
30770 		 * @method disabled
30771 		 * @param {Boolean} [state] State if the control is disabled.
30772 		 * @return {Boolean|tinymce.ui.Widget} True/false or current widget instance.
30773 		 */
30774 		disabled: function(state) {
30775 			var self = this, undef;
30776 
30777 			if (state !== undef) {
30778 				self.aria('disabled', state);
30779 				self.toggleClass('disabled', state);
30780 			}
30781 
30782 			return self._super(state);
30783 		},
30784 
30785 		/**
30786 		 * Called after the control has been rendered.
30787 		 *
30788 		 * @method postRender
30789 		 */
30790 		postRender: function() {
30791 			var self = this, settings = self.settings;
30792 
30793 			self._rendered = true;
30794 
30795 			self._super();
30796 
30797 			if (!self.parent() && (settings.width || settings.height)) {
30798 				self.initLayoutRect();
30799 				self.repaint();
30800 			}
30801 
30802 			if (settings.autofocus) {
30803 				self.focus();
30804 			}
30805 		},
30806 
30807 		/**
30808 		 * Removes the current control from DOM and from UI collections.
30809 		 *
30810 		 * @method remove
30811 		 * @return {tinymce.ui.Control} Current control instance.
30812 		 */
30813 		remove: function() {
30814 			this._super();
30815 
30816 			if (tooltip) {
30817 				tooltip.remove();
30818 				tooltip = null;
30819 			}
30820 		}
30821 	});
30822 
30823 	return Widget;
30824 });
30825 
30826 // Included from: js/tinymce/classes/ui/Button.js
30827 
30828 /**
30829  * Button.js
30830  *
30831  * Copyright, Moxiecode Systems AB
30832  * Released under LGPL License.
30833  *
30834  * License: http://www.tinymce.com/license
30835  * Contributing: http://www.tinymce.com/contributing
30836  */
30837 
30838 /**
30839  * This class is used to create buttons. You can create them directly or through the Factory.
30840  *
30841  * @example
30842  * // Create and render a button to the body element
30843  * tinymce.ui.Factory.create({
30844  *     type: 'button',
30845  *     text: 'My button'
30846  * }).renderTo(document.body);
30847  *
30848  * @-x-less Button.less
30849  * @class tinymce.ui.Button
30850  * @extends tinymce.ui.Widget
30851  */
30852 define("tinymce/ui/Button", [
30853 	"tinymce/ui/Widget"
30854 ], function(Widget) {
30855 	"use strict";
30856 
30857 	return Widget.extend({
30858 		Defaults: {
30859 			classes: "widget btn",
30860 			role: "button"
30861 		},
30862 
30863 		/**
30864 		 * Constructs a new button instance with the specified settings.
30865 		 *
30866 		 * @constructor
30867 		 * @param {Object} settings Name/value object with settings.
30868 		 * @setting {String} size Size of the button small|medium|large.
30869 		 * @setting {String} image Image to use for icon.
30870 		 * @setting {String} icon Icon to use for button.
30871 		 */
30872 		init: function(settings) {
30873 			var self = this, size;
30874 
30875 			self.on('click mousedown', function(e) {
30876 				e.preventDefault();
30877 			});
30878 
30879 			self._super(settings);
30880 			size = settings.size;
30881 
30882 			if (settings.subtype) {
30883 				self.addClass(settings.subtype);
30884 			}
30885 
30886 			if (size) {
30887 				self.addClass('btn-' + size);
30888 			}
30889 		},
30890 
30891 		/**
30892 		 * Sets/gets the current button icon.
30893 		 *
30894 		 * @method icon
30895 		 * @param {String} [icon] New icon identifier.
30896 		 * @return {String|tinymce.ui.MenuButton} Current icon or current MenuButton instance.
30897 		 */
30898 		icon: function(icon) {
30899 			var self = this, prefix = self.classPrefix;
30900 
30901 			if (typeof(icon) == 'undefined') {
30902 				return self.settings.icon;
30903 			}
30904 
30905 			self.settings.icon = icon;
30906 			icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
30907 
30908 			if (self._rendered) {
30909 				var btnElm = self.getEl().firstChild, iconElm = btnElm.getElementsByTagName('i')[0];
30910 
30911 				if (icon) {
30912 					if (!iconElm || iconElm != btnElm.firstChild) {
30913 						iconElm = document.createElement('i');
30914 						btnElm.insertBefore(iconElm, btnElm.firstChild);
30915 					}
30916 
30917 					iconElm.className = icon;
30918 				} else if (iconElm) {
30919 					btnElm.removeChild(iconElm);
30920 				}
30921 
30922 				self.text(self._text); // Set text again to fix whitespace between icon + text
30923 			}
30924 
30925 			return self;
30926 		},
30927 
30928 		/**
30929 		 * Repaints the button for example after it's been resizes by a layout engine.
30930 		 *
30931 		 * @method repaint
30932 		 */
30933 		repaint: function() {
30934 			var btnStyle = this.getEl().firstChild.style;
30935 
30936 			btnStyle.width = btnStyle.height = "100%";
30937 
30938 			this._super();
30939 		},
30940 
30941 		/**
30942 		 * Sets/gets the current button text.
30943 		 *
30944 		 * @method text
30945 		 * @param {String} [text] New button text.
30946 		 * @return {String|tinymce.ui.Button} Current text or current Button instance.
30947 		 */
30948 		text: function(text) {
30949 			var self = this;
30950 
30951 			if (self._rendered) {
30952 				var textNode = self.getEl().lastChild.lastChild;
30953 				if (textNode) {
30954 					textNode.data = self.translate(text);
30955 				}
30956 			}
30957 
30958 			return self._super(text);
30959 		},
30960 
30961 		/**
30962 		 * Renders the control as a HTML string.
30963 		 *
30964 		 * @method renderHtml
30965 		 * @return {String} HTML representing the control.
30966 		 */
30967 		renderHtml: function() {
30968 			var self = this, id = self._id, prefix = self.classPrefix;
30969 			var icon = self.settings.icon, image;
30970 
30971 			image = self.settings.image;
30972 			if (image) {
30973 				icon = 'none';
30974 
30975 				// Support for [high dpi, low dpi] image sources
30976 				if (typeof image != "string") {
30977 					image = window.getSelection ? image[0] : image[1];
30978 				}
30979 
30980 				image = ' style="background-image: url(\'' + image + '\')"';
30981 			} else {
30982 				image = '';
30983 			}
30984 
30985 			icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : '';
30986 
30987 			return (
30988 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1" aria-labelledby="' + id + '">' +
30989 					'<button role="presentation" type="button" tabindex="-1">' +
30990 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
30991 						(self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') +
30992 					'</button>' +
30993 				'</div>'
30994 			);
30995 		}
30996 	});
30997 });
30998 
30999 // Included from: js/tinymce/classes/ui/ButtonGroup.js
31000 
31001 /**
31002  * ButtonGroup.js
31003  *
31004  * Copyright, Moxiecode Systems AB
31005  * Released under LGPL License.
31006  *
31007  * License: http://www.tinymce.com/license
31008  * Contributing: http://www.tinymce.com/contributing
31009  */
31010 
31011 /**
31012  * This control enables you to put multiple buttons into a group. This is
31013  * useful when you want to combine similar toolbar buttons into a group.
31014  *
31015  * @example
31016  * // Create and render a buttongroup with two buttons to the body element
31017  * tinymce.ui.Factory.create({
31018  *     type: 'buttongroup',
31019  *     items: [
31020  *         {text: 'Button A'},
31021  *         {text: 'Button B'}
31022  *     ]
31023  * }).renderTo(document.body);
31024  *
31025  * @-x-less ButtonGroup.less
31026  * @class tinymce.ui.ButtonGroup
31027  * @extends tinymce.ui.Container
31028  */
31029 define("tinymce/ui/ButtonGroup", [
31030 	"tinymce/ui/Container"
31031 ], function(Container) {
31032 	"use strict";
31033 
31034 	return Container.extend({
31035 		Defaults: {
31036 			defaultType: 'button',
31037 			role: 'group'
31038 		},
31039 
31040 		/**
31041 		 * Renders the control as a HTML string.
31042 		 *
31043 		 * @method renderHtml
31044 		 * @return {String} HTML representing the control.
31045 		 */
31046 		renderHtml: function() {
31047 			var self = this, layout = self._layout;
31048 
31049 			self.addClass('btn-group');
31050 			self.preRender();
31051 			layout.preRender(self);
31052 
31053 			return (
31054 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
31055 					'<div id="' + self._id + '-body">' +
31056 						(self.settings.html || '') + layout.renderHtml(self) +
31057 					'</div>' +
31058 				'</div>'
31059 			);
31060 		}
31061 	});
31062 });
31063 
31064 // Included from: js/tinymce/classes/ui/Checkbox.js
31065 
31066 /**
31067  * Checkbox.js
31068  *
31069  * Copyright, Moxiecode Systems AB
31070  * Released under LGPL License.
31071  *
31072  * License: http://www.tinymce.com/license
31073  * Contributing: http://www.tinymce.com/contributing
31074  */
31075 
31076 /**
31077  * This control creates a custom checkbox.
31078  *
31079  * @example
31080  * // Create and render a checkbox to the body element
31081  * tinymce.ui.Factory.create({
31082  *     type: 'checkbox',
31083  *     checked: true,
31084  *     text: 'My checkbox'
31085  * }).renderTo(document.body);
31086  *
31087  * @-x-less Checkbox.less
31088  * @class tinymce.ui.Checkbox
31089  * @extends tinymce.ui.Widget
31090  */
31091 define("tinymce/ui/Checkbox", [
31092 	"tinymce/ui/Widget"
31093 ], function(Widget) {
31094 	"use strict";
31095 
31096 	return Widget.extend({
31097 		Defaults: {
31098 			classes: "checkbox",
31099 			role: "checkbox",
31100 			checked: false
31101 		},
31102 
31103 		/**
31104 		 * Constructs a new Checkbox instance with the specified settings.
31105 		 *
31106 		 * @constructor
31107 		 * @param {Object} settings Name/value object with settings.
31108 		 * @setting {Boolean} checked True if the checkbox should be checked by default.
31109 		 */
31110 		init: function(settings) {
31111 			var self = this;
31112 
31113 			self._super(settings);
31114 
31115 			self.on('click mousedown', function(e) {
31116 				e.preventDefault();
31117 			});
31118 
31119 			self.on('click', function(e) {
31120 				e.preventDefault();
31121 
31122 				if (!self.disabled()) {
31123 					self.checked(!self.checked());
31124 				}
31125 			});
31126 
31127 			self.checked(self.settings.checked);
31128 		},
31129 
31130 		/**
31131 		 * Getter/setter function for the checked state.
31132 		 *
31133 		 * @method checked
31134 		 * @param {Boolean} [state] State to be set.
31135 		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
31136 		 */
31137 		checked: function(state) {
31138 			var self = this;
31139 
31140 			if (typeof state != "undefined") {
31141 				if (state) {
31142 					self.addClass('checked');
31143 				} else {
31144 					self.removeClass('checked');
31145 				}
31146 
31147 				self._checked = state;
31148 				self.aria('checked', state);
31149 
31150 				return self;
31151 			}
31152 
31153 			return self._checked;
31154 		},
31155 
31156 		/**
31157 		 * Getter/setter function for the value state.
31158 		 *
31159 		 * @method value
31160 		 * @param {Boolean} [state] State to be set.
31161 		 * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation.
31162 		 */
31163 		value: function(state) {
31164 			return this.checked(state);
31165 		},
31166 
31167 		/**
31168 		 * Renders the control as a HTML string.
31169 		 *
31170 		 * @method renderHtml
31171 		 * @return {String} HTML representing the control.
31172 		 */
31173 		renderHtml: function() {
31174 			var self = this, id = self._id, prefix = self.classPrefix;
31175 
31176 			return (
31177 				'<div id="' + id + '" class="' + self.classes() + '" unselectable="on" aria-labelledby="' + id + '-al" tabindex="-1">' +
31178 					'<i class="' + prefix + 'ico ' + prefix + 'i-checkbox"></i>' +
31179 					'<span id="' + id + '-al" class="' + prefix + 'label">' + self.encode(self._text) + '</span>' +
31180 				'</div>'
31181 			);
31182 		}
31183 	});
31184 });
31185 
31186 // Included from: js/tinymce/classes/ui/PanelButton.js
31187 
31188 /**
31189  * PanelButton.js
31190  *
31191  * Copyright, Moxiecode Systems AB
31192  * Released under LGPL License.
31193  *
31194  * License: http://www.tinymce.com/license
31195  * Contributing: http://www.tinymce.com/contributing
31196  */
31197 
31198 /**
31199  * Creates a new panel button.
31200  *
31201  * @class tinymce.ui.PanelButton
31202  * @extends tinymce.ui.Button
31203  */
31204 define("tinymce/ui/PanelButton", [
31205 	"tinymce/ui/Button",
31206 	"tinymce/ui/FloatPanel"
31207 ], function(Button, FloatPanel) {
31208 	"use strict";
31209 
31210 	return Button.extend({
31211 		/**
31212 		 * Shows the panel for the button.
31213 		 *
31214 		 * @method showPanel
31215 		 */
31216 		showPanel: function() {
31217 			var self = this, settings = self.settings;
31218 
31219 			self.active(true);
31220 
31221 			if (!self.panel) {
31222 				var panelSettings = settings.panel;
31223 
31224 				// Wrap panel in grid layout if type if specified
31225 				// This makes it possible to add forms or other containers directly in the panel option
31226 				if (panelSettings.type) {
31227 					panelSettings = {
31228 						layout: 'grid',
31229 						items: panelSettings
31230 					};
31231 				}
31232 
31233 				panelSettings.role = panelSettings.role || 'dialog';
31234 				panelSettings.popover = true;
31235 				panelSettings.autohide = true;
31236 				panelSettings.ariaRoot = true;
31237 
31238 				self.panel = new FloatPanel(panelSettings).on('hide', function() {
31239 					self.active(false);
31240 				}).on('cancel', function(e) {
31241 					e.stopPropagation();
31242 					self.focus();
31243 					self.hidePanel();
31244 				}).parent(self).renderTo(self.getContainerElm());
31245 
31246 				self.panel.fire('show');
31247 				self.panel.reflow();
31248 			} else {
31249 				self.panel.show();
31250 			}
31251 
31252 			self.panel.moveRel(self.getEl(), settings.popoverAlign || (self.isRtl() ? ['bc-tr', 'bc-tc'] : ['bc-tl', 'bc-tc']));
31253 		},
31254 
31255 		/**
31256 		 * Hides the panel for the button.
31257 		 *
31258 		 * @method hidePanel
31259 		 */
31260 		hidePanel: function() {
31261 			var self = this;
31262 
31263 			if (self.panel) {
31264 				self.panel.hide();
31265 			}
31266 		},
31267 
31268 		/**
31269 		 * Called after the control has been rendered.
31270 		 *
31271 		 * @method postRender
31272 		 */
31273 		postRender: function() {
31274 			var self = this;
31275 
31276 			self.aria('haspopup', true);
31277 
31278 			self.on('click', function(e) {
31279 				if (e.control === self) {
31280 					if (self.panel && self.panel.visible()) {
31281 						self.hidePanel();
31282 					} else {
31283 						self.showPanel();
31284 						self.panel.focus(!!e.aria);
31285 					}
31286 				}
31287 			});
31288 
31289 			return self._super();
31290 		}
31291 	});
31292 });
31293 
31294 // Included from: js/tinymce/classes/ui/ColorButton.js
31295 
31296 /**
31297  * ColorButton.js
31298  *
31299  * Copyright, Moxiecode Systems AB
31300  * Released under LGPL License.
31301  *
31302  * License: http://www.tinymce.com/license
31303  * Contributing: http://www.tinymce.com/contributing
31304  */
31305 
31306 /**
31307  * This class creates a color button control. This is a split button in which the main
31308  * button has a visual representation of the currently selected color. When clicked 
31309  * the caret button displays a color picker, allowing the user to select a new color.
31310  *
31311  * @-x-less ColorButton.less
31312  * @class tinymce.ui.ColorButton
31313  * @extends tinymce.ui.PanelButton
31314  */
31315 define("tinymce/ui/ColorButton", [
31316 	"tinymce/ui/PanelButton",
31317 	"tinymce/dom/DOMUtils"
31318 ], function(PanelButton, DomUtils) {
31319 	"use strict";
31320 	
31321 	var DOM = DomUtils.DOM;
31322 
31323 	return PanelButton.extend({
31324 		/**
31325 		 * Constructs a new ColorButton instance with the specified settings.
31326 		 *
31327 		 * @constructor
31328 		 * @param {Object} settings Name/value object with settings.
31329 		 */
31330 		init: function(settings) {
31331 			this._super(settings);
31332 			this.addClass('colorbutton');
31333 		},
31334 
31335 		/**
31336 		 * Getter/setter for the current color.
31337 		 *
31338 		 * @method color
31339 		 * @param {String} [color] Color to set.
31340 		 * @return {String|tinymce.ui.ColorButton} Current color or current instance.
31341 		 */
31342 		color: function(color) {
31343 			if (color) {
31344 				this._color = color;
31345 				this.getEl('preview').style.backgroundColor = color;
31346 				return this;
31347 			}
31348 
31349 			return this._color;
31350 		},
31351 
31352 		/**
31353 		 * Renders the control as a HTML string.
31354 		 *
31355 		 * @method renderHtml
31356 		 * @return {String} HTML representing the control.
31357 		 */
31358 		renderHtml: function() {
31359 			var self = this, id = self._id, prefix = self.classPrefix;
31360 			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
31361 			var image = self.settings.image ? ' style="background-image: url(\'' + self.settings.image + '\')"' : '';
31362 
31363 			return (
31364 				'<div id="' + id + '" class="' + self.classes() + '" role="button" tabindex="-1" aria-haspopup="true">' +
31365 					'<button role="presentation" hidefocus="1" type="button" tabindex="-1">' +
31366 						(icon ? '<i class="' + icon + '"' + image + '></i>' : '') +
31367 						'<span id="' + id + '-preview" class="' + prefix + 'preview"></span>' +
31368 						(self._text ? (icon ? ' ' : '') + (self._text) : '') +
31369 					'</button>' +
31370 					'<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' +
31371 						' <i class="' + prefix + 'caret"></i>' +
31372 					'</button>' +
31373 				'</div>'
31374 			);
31375 		},
31376 		
31377 		/**
31378 		 * Called after the control has been rendered.
31379 		 *
31380 		 * @method postRender
31381 		 */
31382 		postRender: function() {
31383 			var self = this, onClickHandler = self.settings.onclick;
31384 
31385 			self.on('click', function(e) {
31386 				if (e.aria && e.aria.key == 'down') {
31387 					return;
31388 				}
31389 
31390 				if (e.control == self && !DOM.getParent(e.target, '.' + self.classPrefix + 'open')) {
31391 					e.stopImmediatePropagation();
31392 					onClickHandler.call(self, e);
31393 				}
31394 			});
31395 
31396 			delete self.settings.onclick;
31397 
31398 			return self._super();
31399 		}
31400 		
31401 	});
31402 });
31403 
31404 // Included from: js/tinymce/classes/ui/ComboBox.js
31405 
31406 /**
31407  * ComboBox.js
31408  *
31409  * Copyright, Moxiecode Systems AB
31410  * Released under LGPL License.
31411  *
31412  * License: http://www.tinymce.com/license
31413  * Contributing: http://www.tinymce.com/contributing
31414  */
31415 
31416 /**
31417  * This class creates a combobox control. Select box that you select a value from or
31418  * type a value into.
31419  *
31420  * @-x-less ComboBox.less
31421  * @class tinymce.ui.ComboBox
31422  * @extends tinymce.ui.Widget
31423  */
31424 define("tinymce/ui/ComboBox", [
31425 	"tinymce/ui/Widget",
31426 	"tinymce/ui/Factory",
31427 	"tinymce/ui/DomUtils"
31428 ], function(Widget, Factory, DomUtils) {
31429 	"use strict";
31430 
31431 	return Widget.extend({
31432 		/**
31433 		 * Constructs a new control instance with the specified settings.
31434 		 *
31435 		 * @constructor
31436 		 * @param {Object} settings Name/value object with settings.
31437 		 * @setting {String} placeholder Placeholder text to display.
31438 		 */
31439 		init: function(settings) {
31440 			var self = this;
31441 
31442 			self._super(settings);
31443 			self.addClass('combobox');
31444 			self.subinput = true;
31445 			self.ariaTarget = 'inp'; // TODO: Figure out a better way
31446 
31447 			settings = self.settings;
31448 			settings.menu = settings.menu || settings.values;
31449 
31450 			if (settings.menu) {
31451 				settings.icon = 'caret';
31452 			}
31453 
31454 			self.on('click', function(e) {
31455 				var elm = e.target, root = self.getEl();
31456 
31457 				while (elm && elm != root) {
31458 					if (elm.id && elm.id.indexOf('-open') != -1) {
31459 						self.fire('action');
31460 
31461 						if (settings.menu) {
31462 							self.showMenu();
31463 
31464 							if (e.aria) {
31465 								self.menu.items()[0].focus();
31466 							}
31467 						}
31468 					}
31469 
31470 					elm = elm.parentNode;
31471 				}
31472 			});
31473 
31474 			// TODO: Rework this
31475 			self.on('keydown', function(e) {
31476 				if (e.target.nodeName == "INPUT" && e.keyCode == 13) {
31477 					self.parents().reverse().each(function(ctrl) {
31478 						e.preventDefault();
31479 						self.fire('change');
31480 
31481 						if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
31482 							ctrl.fire('submit', {data: ctrl.toJSON()});
31483 							return false;
31484 						}
31485 					});
31486 				}
31487 			});
31488 
31489 			if (settings.placeholder) {
31490 				self.addClass('placeholder');
31491 
31492 				self.on('focusin', function() {
31493 					if (!self._hasOnChange) {
31494 						DomUtils.on(self.getEl('inp'), 'change', function() {
31495 							self.fire('change');
31496 						});
31497 
31498 						self._hasOnChange = true;
31499 					}
31500 
31501 					if (self.hasClass('placeholder')) {
31502 						self.getEl('inp').value = '';
31503 						self.removeClass('placeholder');
31504 					}
31505 				});
31506 
31507 				self.on('focusout', function() {
31508 					if (self.value().length === 0) {
31509 						self.getEl('inp').value = settings.placeholder;
31510 						self.addClass('placeholder');
31511 					}
31512 				});
31513 			}
31514 		},
31515 
31516 		showMenu: function() {
31517 			var self = this, settings = self.settings, menu;
31518 
31519 			if (!self.menu) {
31520 				menu = settings.menu || [];
31521 
31522 				// Is menu array then auto constuct menu control
31523 				if (menu.length) {
31524 					menu = {
31525 						type: 'menu',
31526 						items: menu
31527 					};
31528 				} else {
31529 					menu.type = menu.type || 'menu';
31530 				}
31531 
31532 				self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm());
31533 				self.fire('createmenu');
31534 				self.menu.reflow();
31535 				self.menu.on('cancel', function(e) {
31536 					if (e.control === self.menu) {
31537 						self.focus();
31538 					}
31539 				});
31540 
31541 				self.menu.on('show hide', function(e) {
31542 					e.control.items().each(function(ctrl) {
31543 						ctrl.active(ctrl.value() == self.value());
31544 					});
31545 				}).fire('show');
31546 
31547 				self.menu.on('select', function(e) {
31548 					self.value(e.control.value());
31549 				});
31550 
31551 				self.on('focusin', function(e) {
31552 					if (e.target.tagName.toUpperCase() == 'INPUT') {
31553 						self.menu.hide();
31554 					}
31555 				});
31556 
31557 				self.aria('expanded', true);
31558 			}
31559 
31560 			self.menu.show();
31561 			self.menu.layoutRect({w: self.layoutRect().w});
31562 			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
31563 		},
31564 
31565 		/**
31566 		 * Getter/setter function for the control value.
31567 		 *
31568 		 * @method value
31569 		 * @param {String} [value] Value to be set.
31570 		 * @return {String|tinymce.ui.ComboBox} Value or self if it's a set operation.
31571 		 */
31572 		value: function(value) {
31573 			var self = this;
31574 
31575 			if (typeof(value) != "undefined") {
31576 				self._value = value;
31577 				self.removeClass('placeholder');
31578 
31579 				if (self._rendered) {
31580 					self.getEl('inp').value = value;
31581 				}
31582 
31583 				return self;
31584 			}
31585 
31586 			if (self._rendered) {
31587 				value = self.getEl('inp').value;
31588 
31589 				if (value != self.settings.placeholder) {
31590 					return value;
31591 				}
31592 
31593 				return '';
31594 			}
31595 
31596 			return self._value;
31597 		},
31598 
31599 		/**
31600 		 * Getter/setter function for the disabled state.
31601 		 *
31602 		 * @method value
31603 		 * @param {Boolean} [state] State to be set.
31604 		 * @return {Boolean|tinymce.ui.ComboBox} True/false or self if it's a set operation.
31605 		 */
31606 		disabled: function(state) {
31607 			var self = this;
31608 
31609 			if (self._rendered && typeof(state) != 'undefined') {
31610 				self.getEl('inp').disabled = state;
31611 			}
31612 
31613 			return self._super(state);
31614 		},
31615 
31616 		/**
31617 		 * Focuses the input area of the control.
31618 		 *
31619 		 * @method focus
31620 		 */
31621 		focus: function() {
31622 			this.getEl('inp').focus();
31623 		},
31624 
31625 		/**
31626 		 * Repaints the control after a layout operation.
31627 		 *
31628 		 * @method repaint
31629 		 */
31630 		repaint: function() {
31631 			var self = this, elm = self.getEl(), openElm = self.getEl('open'), rect = self.layoutRect();
31632 			var width, lineHeight;
31633 
31634 			if (openElm) {
31635 				width = rect.w - DomUtils.getSize(openElm).width - 10;
31636 			} else {
31637 				width = rect.w - 10;
31638 			}
31639 
31640 			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
31641 			var doc = document;
31642 			if (doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
31643 				lineHeight = (self.layoutRect().h - 2) + 'px';
31644 			}
31645 
31646 			DomUtils.css(elm.firstChild, {
31647 				width: width,
31648 				lineHeight: lineHeight
31649 			});
31650 
31651 			self._super();
31652 
31653 			return self;
31654 		},
31655 
31656 		/**
31657 		 * Post render method. Called after the control has been rendered to the target.
31658 		 *
31659 		 * @method postRender
31660 		 * @return {tinymce.ui.ComboBox} Current combobox instance.
31661 		 */
31662 		postRender: function() {
31663 			var self = this;
31664 
31665 			DomUtils.on(this.getEl('inp'), 'change', function() {
31666 				self.fire('change');
31667 			});
31668 
31669 			return self._super();
31670 		},
31671 
31672 		remove: function() {
31673 			DomUtils.off(this.getEl('inp'));
31674 			this._super();
31675 		},
31676 
31677 		/**
31678 		 * Renders the control as a HTML string.
31679 		 *
31680 		 * @method renderHtml
31681 		 * @return {String} HTML representing the control.
31682 		 */
31683 		renderHtml: function() {
31684 			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix;
31685 			var value = settings.value || settings.placeholder || '';
31686 			var icon, text, openBtnHtml = '', extraAttrs = '';
31687 
31688 			if ("spellcheck" in settings) {
31689 				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
31690 			}
31691 
31692 			if (settings.maxLength) {
31693 				extraAttrs += ' maxlength="' + settings.maxLength + '"';
31694 			}
31695 
31696 			if (settings.size) {
31697 				extraAttrs += ' size="' + settings.size + '"';
31698 			}
31699 
31700 			if (settings.subtype) {
31701 				extraAttrs += ' type="' + settings.subtype + '"';
31702 			}
31703 
31704 			if (self.disabled()) {
31705 				extraAttrs += ' disabled="disabled"';
31706 			}
31707 
31708 			icon = settings.icon;
31709 			if (icon && icon != 'caret') {
31710 				icon = prefix + 'ico ' + prefix + 'i-' + settings.icon;
31711 			}
31712 
31713 			text = self._text;
31714 
31715 			if (icon || text) {
31716 				openBtnHtml = (
31717 					'<div id="' + id + '-open" class="' + prefix + 'btn ' + prefix + 'open" tabIndex="-1" role="button">' +
31718 						'<button id="' + id + '-action" type="button" hidefocus="1" tabindex="-1">' +
31719 							(icon != 'caret' ? '<i class="' + icon + '"></i>' : '<i class="' + prefix + 'caret"></i>') +
31720 							(text ? (icon ? ' ' : '') + text : '') +
31721 						'</button>' +
31722 					'</div>'
31723 				);
31724 
31725 				self.addClass('has-open');
31726 			}
31727 
31728 			return (
31729 				'<div id="' + id + '" class="' + self.classes() + '">' +
31730 					'<input id="' + id + '-inp" class="' + prefix + 'textbox ' + prefix + 'placeholder" value="' +
31731 					value + '" hidefocus="1"' + extraAttrs + ' />' +
31732 					openBtnHtml +
31733 				'</div>'
31734 			);
31735 		}
31736 	});
31737 });
31738 
31739 // Included from: js/tinymce/classes/ui/Path.js
31740 
31741 /**
31742  * Path.js
31743  *
31744  * Copyright, Moxiecode Systems AB
31745  * Released under LGPL License.
31746  *
31747  * License: http://www.tinymce.com/license
31748  * Contributing: http://www.tinymce.com/contributing
31749  */
31750 
31751 /**
31752  * Creates a new path control.
31753  *
31754  * @-x-less Path.less
31755  * @class tinymce.ui.Path
31756  * @extends tinymce.ui.Widget
31757  */
31758 define("tinymce/ui/Path", [
31759 	"tinymce/ui/Widget"
31760 ], function(Widget) {
31761 	"use strict";
31762 
31763 	return Widget.extend({
31764 		/**
31765 		 * Constructs a instance with the specified settings.
31766 		 *
31767 		 * @constructor
31768 		 * @param {Object} settings Name/value object with settings.
31769 		 * @setting {String} delimiter Delimiter to display between items in path.
31770 		 */
31771 		init: function(settings) {
31772 			var self = this;
31773 
31774 			if (!settings.delimiter) {
31775 				settings.delimiter = '\u00BB';
31776 			}
31777 
31778 			self._super(settings);
31779 			self.addClass('path');
31780 			self.canFocus = true;
31781 
31782 			self.on('click', function(e) {
31783 				var index, target = e.target;
31784 
31785 				if ((index = target.getAttribute('data-index'))) {
31786 					self.fire('select', {value: self.data()[index], index: index});
31787 				}
31788 			});
31789 		},
31790 
31791 		/**
31792 		 * Focuses the current control.
31793 		 *
31794 		 * @method focus
31795 		 * @return {tinymce.ui.Control} Current control instance.
31796 		 */
31797 		focus: function() {
31798 			var self = this;
31799 
31800 			self.getEl().firstChild.focus();
31801 
31802 			return self;
31803 		},
31804 
31805 		/**
31806 		 * Sets/gets the data to be used for the path.
31807 		 *
31808 		 * @method data
31809 		 * @param {Array} data Array with items name is rendered to path.
31810 		 */
31811 		data: function(data) {
31812 			var self = this;
31813 
31814 			if (typeof(data) !== "undefined") {
31815 				self._data = data;
31816 				self.update();
31817 
31818 				return self;
31819 			}
31820 
31821 			return self._data;
31822 		},
31823 
31824 		/**
31825 		 * Updated the path.
31826 		 *
31827 		 * @private
31828 		 */
31829 		update: function() {
31830 			this.innerHtml(this._getPathHtml());
31831 		},
31832 
31833 		/**
31834 		 * Called after the control has been rendered.
31835 		 *
31836 		 * @method postRender
31837 		 */
31838 		postRender: function() {
31839 			var self = this;
31840 
31841 			self._super();
31842 
31843 			self.data(self.settings.data);
31844 		},
31845 
31846 		/**
31847 		 * Renders the control as a HTML string.
31848 		 *
31849 		 * @method renderHtml
31850 		 * @return {String} HTML representing the control.
31851 		 */
31852 		renderHtml: function() {
31853 			var self = this;
31854 
31855 			return (
31856 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
31857 					self._getPathHtml() +
31858 				'</div>'
31859 			);
31860 		},
31861 
31862 		_getPathHtml: function() {
31863 			var self = this, parts = self._data || [], i, l, html = '', prefix = self.classPrefix;
31864 
31865 			for (i = 0, l = parts.length; i < l; i++) {
31866 				html += (
31867 					(i > 0 ? '<div class="' + prefix + 'divider" aria-hidden="true"> ' + self.settings.delimiter + ' </div>' : '') +
31868 					'<div role="button" class="' + prefix + 'path-item' + (i == l - 1 ? ' ' + prefix + 'last' : '') + '" data-index="' +
31869 					i + '" tabindex="-1" id="' + self._id + '-' + i + '" aria-level="' + i + '">' + parts[i].name + '</div>'
31870 				);
31871 			}
31872 
31873 			if (!html) {
31874 				html = '<div class="' + prefix + 'path-item">\u00a0</div>';
31875 			}
31876 
31877 			return html;
31878 		}
31879 	});
31880 });
31881 
31882 // Included from: js/tinymce/classes/ui/ElementPath.js
31883 
31884 /**
31885  * ElementPath.js
31886  *
31887  * Copyright, Moxiecode Systems AB
31888  * Released under LGPL License.
31889  *
31890  * License: http://www.tinymce.com/license
31891  * Contributing: http://www.tinymce.com/contributing
31892  */
31893 
31894 /**
31895  * This control creates an path for the current selections parent elements in TinyMCE.
31896  *
31897  * @class tinymce.ui.ElementPath
31898  * @extends tinymce.ui.Path
31899  */
31900 define("tinymce/ui/ElementPath", [
31901 	"tinymce/ui/Path",
31902 	"tinymce/EditorManager"
31903 ], function(Path, EditorManager) {
31904 	return Path.extend({
31905 		/**
31906 		 * Post render method. Called after the control has been rendered to the target.
31907 		 *
31908 		 * @method postRender
31909 		 * @return {tinymce.ui.ElementPath} Current combobox instance.
31910 		 */
31911 		postRender: function() {
31912 			var self = this, editor = EditorManager.activeEditor;
31913 
31914 			function isHidden(elm) {
31915 				if (elm.nodeType === 1) {
31916 					if (elm.nodeName == "BR" || !!elm.getAttribute('data-mce-bogus')) {
31917 						return true;
31918 					}
31919 
31920 					if (elm.getAttribute('data-mce-type') === 'bookmark') {
31921 						return true;
31922 					}
31923 				}
31924 
31925 				return false;
31926 			}
31927 
31928 			self.on('select', function(e) {
31929 				var parents = [], node, body = editor.getBody();
31930 
31931 				editor.focus();
31932 
31933 				node = editor.selection.getStart();
31934 				while (node && node != body) {
31935 					if (!isHidden(node)) {
31936 						parents.push(node);
31937 					}
31938 
31939 					node = node.parentNode;
31940 				}
31941 
31942 				editor.selection.select(parents[parents.length - 1 - e.index]);
31943 				editor.nodeChanged();
31944 			});
31945 
31946 			editor.on('nodeChange', function(e) {
31947 				var parents = [], selectionParents = e.parents, i = selectionParents.length;
31948 
31949 				while (i--) {
31950 					if (selectionParents[i].nodeType == 1 && !isHidden(selectionParents[i])) {
31951 						var args = editor.fire('ResolveName', {
31952 							name: selectionParents[i].nodeName.toLowerCase(),
31953 							target: selectionParents[i]
31954 						});
31955 
31956 						parents.push({name: args.name});
31957 					}
31958 				}
31959 
31960 				self.data(parents);
31961 			});
31962 
31963 			return self._super();
31964 		}
31965 	});
31966 });
31967 
31968 // Included from: js/tinymce/classes/ui/FormItem.js
31969 
31970 /**
31971  * FormItem.js
31972  *
31973  * Copyright, Moxiecode Systems AB
31974  * Released under LGPL License.
31975  *
31976  * License: http://www.tinymce.com/license
31977  * Contributing: http://www.tinymce.com/contributing
31978  */
31979 
31980 /**
31981  * This class is a container created by the form element with
31982  * a label and control item.
31983  *
31984  * @class tinymce.ui.FormItem
31985  * @extends tinymce.ui.Container
31986  * @setting {String} label Label to display for the form item.
31987  */
31988 define("tinymce/ui/FormItem", [
31989 	"tinymce/ui/Container"
31990 ], function(Container) {
31991 	"use strict";
31992 
31993 	return Container.extend({
31994 		Defaults: {
31995 			layout: 'flex',
31996 			align: 'center',
31997 			defaults: {
31998 				flex: 1
31999 			}
32000 		},
32001 
32002 		/**
32003 		 * Renders the control as a HTML string.
32004 		 *
32005 		 * @method renderHtml
32006 		 * @return {String} HTML representing the control.
32007 		 */
32008 		renderHtml: function() {
32009 			var self = this, layout = self._layout, prefix = self.classPrefix;
32010 
32011 			self.addClass('formitem');
32012 			layout.preRender(self);
32013 
32014 			return (
32015 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
32016 					(self.settings.title ? ('<div id="' + self._id + '-title" class="' + prefix + 'title">' +
32017 						self.settings.title + '</div>') : '') +
32018 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
32019 						(self.settings.html || '') + layout.renderHtml(self) +
32020 					'</div>' +
32021 				'</div>'
32022 			);
32023 		}
32024 	});
32025 });
32026 
32027 // Included from: js/tinymce/classes/ui/Form.js
32028 
32029 /**
32030  * Form.js
32031  *
32032  * Copyright, Moxiecode Systems AB
32033  * Released under LGPL License.
32034  *
32035  * License: http://www.tinymce.com/license
32036  * Contributing: http://www.tinymce.com/contributing
32037  */
32038 
32039 /**
32040  * This class creates a form container. A form container has the ability
32041  * to automatically wrap items in tinymce.ui.FormItem instances.
32042  *
32043  * Each FormItem instance is a container for the label and the item.
32044  *
32045  * @example
32046  * tinymce.ui.Factory.create({
32047  *     type: 'form',
32048  *     items: [
32049  *         {type: 'textbox', label: 'My text box'}
32050  *     ]
32051  * }).renderTo(document.body);
32052  *
32053  * @class tinymce.ui.Form
32054  * @extends tinymce.ui.Container
32055  */
32056 define("tinymce/ui/Form", [
32057 	"tinymce/ui/Container",
32058 	"tinymce/ui/FormItem"
32059 ], function(Container, FormItem) {
32060 	"use strict";
32061 
32062 	return Container.extend({
32063 		Defaults: {
32064 			containerCls: 'form',
32065 			layout: 'flex',
32066 			direction: 'column',
32067 			align: 'stretch',
32068 			flex: 1,
32069 			padding: 20,
32070 			labelGap: 30,
32071 			spacing: 10,
32072 			callbacks: {
32073 				submit: function() {
32074 					this.submit();
32075 				}
32076 			}
32077 		},
32078 
32079 		/**
32080 		 * This method gets invoked before the control is rendered.
32081 		 *
32082 		 * @method preRender
32083 		 */
32084 		preRender: function() {
32085 			var self = this, items = self.items();
32086 
32087 			// Wrap any labeled items in FormItems
32088 			items.each(function(ctrl) {
32089 				var formItem, label = ctrl.settings.label;
32090 
32091 				if (label) {
32092 					formItem = new FormItem({
32093 						layout: 'flex',
32094 						autoResize: "overflow",
32095 						defaults: {flex: 1},
32096 						items: [
32097 							{type: 'label', id: ctrl._id + '-l', text: label, flex: 0, forId: ctrl._id, disabled: ctrl.disabled()}
32098 						]
32099 					});
32100 
32101 					formItem.type = 'formitem';
32102 					ctrl.aria('labelledby', ctrl._id + '-l');
32103 
32104 					if (typeof(ctrl.settings.flex) == "undefined") {
32105 						ctrl.settings.flex = 1;
32106 					}
32107 
32108 					self.replace(ctrl, formItem);
32109 					formItem.add(ctrl);
32110 				}
32111 			});
32112 		},
32113 
32114 		/**
32115 		 * Recalcs label widths.
32116 		 *
32117 		 * @private
32118 		 */
32119 		recalcLabels: function() {
32120 			var self = this, maxLabelWidth = 0, labels = [], i, labelGap;
32121 
32122 			if (self.settings.labelGapCalc === false) {
32123 				return;
32124 			}
32125 
32126 			self.items().filter('formitem').each(function(item) {
32127 				var labelCtrl = item.items()[0], labelWidth = labelCtrl.getEl().clientWidth;
32128 
32129 				maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth;
32130 				labels.push(labelCtrl);
32131 			});
32132 
32133 			labelGap = self.settings.labelGap || 0;
32134 
32135 			i = labels.length;
32136 			while (i--) {
32137 				labels[i].settings.minWidth = maxLabelWidth + labelGap;
32138 			}
32139 		},
32140 
32141 		/**
32142 		 * Getter/setter for the visibility state.
32143 		 *
32144 		 * @method visible
32145 		 * @param {Boolean} [state] True/false state to show/hide.
32146 		 * @return {tinymce.ui.Form|Boolean} True/false state or current control.
32147 		 */
32148 		visible: function(state) {
32149 			var val = this._super(state);
32150 
32151 			if (state === true && this._rendered) {
32152 				this.recalcLabels();
32153 			}
32154 
32155 			return val;
32156 		},
32157 
32158 		/**
32159 		 * Fires a submit event with the serialized form.
32160 		 *
32161 		 * @method submit
32162 		 * @return {Object} Event arguments object.
32163 		 */
32164 		submit: function() {
32165 			return this.fire('submit', {data: this.toJSON()});
32166 		},
32167 
32168 		/**
32169 		 * Post render method. Called after the control has been rendered to the target.
32170 		 *
32171 		 * @method postRender
32172 		 * @return {tinymce.ui.ComboBox} Current combobox instance.
32173 		 */
32174 		postRender: function() {
32175 			var self = this;
32176 
32177 			self._super();
32178 			self.recalcLabels();
32179 			self.fromJSON(self.settings.data);
32180 		}
32181 	});
32182 });
32183 
32184 // Included from: js/tinymce/classes/ui/FieldSet.js
32185 
32186 /**
32187  * FieldSet.js
32188  *
32189  * Copyright, Moxiecode Systems AB
32190  * Released under LGPL License.
32191  *
32192  * License: http://www.tinymce.com/license
32193  * Contributing: http://www.tinymce.com/contributing
32194  */
32195 
32196 /**
32197  * This class creates fieldset containers.
32198  *
32199  * @-x-less FieldSet.less
32200  * @class tinymce.ui.FieldSet
32201  * @extends tinymce.ui.Form
32202  */
32203 define("tinymce/ui/FieldSet", [
32204 	"tinymce/ui/Form"
32205 ], function(Form) {
32206 	"use strict";
32207 
32208 	return Form.extend({
32209 		Defaults: {
32210 			containerCls: 'fieldset',
32211 			layout: 'flex',
32212 			direction: 'column',
32213 			align: 'stretch',
32214 			flex: 1,
32215 			padding: "25 15 5 15",
32216 			labelGap: 30,
32217 			spacing: 10,
32218 			border: 1
32219 		},
32220 
32221 		/**
32222 		 * Renders the control as a HTML string.
32223 		 *
32224 		 * @method renderHtml
32225 		 * @return {String} HTML representing the control.
32226 		 */
32227 		renderHtml: function() {
32228 			var self = this, layout = self._layout, prefix = self.classPrefix;
32229 
32230 			self.preRender();
32231 			layout.preRender(self);
32232 
32233 			return (
32234 				'<fieldset id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
32235 					(self.settings.title ? ('<legend id="' + self._id + '-title" class="' + prefix + 'fieldset-title">' +
32236 						self.settings.title + '</legend>') : '') +
32237 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
32238 						(self.settings.html || '') + layout.renderHtml(self) +
32239 					'</div>' +
32240 				'</fieldset>'
32241 			);
32242 		}
32243 	});
32244 });
32245 
32246 // Included from: js/tinymce/classes/ui/FilePicker.js
32247 
32248 /**
32249  * FilePicker.js
32250  *
32251  * Copyright, Moxiecode Systems AB
32252  * Released under LGPL License.
32253  *
32254  * License: http://www.tinymce.com/license
32255  * Contributing: http://www.tinymce.com/contributing
32256  */
32257 
32258 /*global tinymce:true */
32259 
32260 /**
32261  * This class creates a file picker control.
32262  *
32263  * @class tinymce.ui.FilePicker
32264  * @extends tinymce.ui.ComboBox
32265  */
32266 define("tinymce/ui/FilePicker", [
32267 	"tinymce/ui/ComboBox"
32268 ], function(ComboBox) {
32269 	"use strict";
32270 
32271 	return ComboBox.extend({
32272 		/**
32273 		 * Constructs a new control instance with the specified settings.
32274 		 *
32275 		 * @constructor
32276 		 * @param {Object} settings Name/value object with settings.
32277 		 */
32278 		init: function(settings) {
32279 			var self = this, editor = tinymce.activeEditor, fileBrowserCallback;
32280 
32281 			settings.spellcheck = false;
32282 
32283 			fileBrowserCallback = editor.settings.file_browser_callback;
32284 			if (fileBrowserCallback) {
32285 				settings.icon = 'browse';
32286 
32287 				settings.onaction = function() {
32288 					fileBrowserCallback(
32289 						self.getEl('inp').id,
32290 						self.getEl('inp').value,
32291 						settings.filetype,
32292 						window
32293 					);
32294 				};
32295 			}
32296 
32297 			self._super(settings);
32298 		}
32299 	});
32300 });
32301 
32302 // Included from: js/tinymce/classes/ui/FitLayout.js
32303 
32304 /**
32305  * FitLayout.js
32306  *
32307  * Copyright, Moxiecode Systems AB
32308  * Released under LGPL License.
32309  *
32310  * License: http://www.tinymce.com/license
32311  * Contributing: http://www.tinymce.com/contributing
32312  */
32313 
32314 /**
32315  * This layout manager will resize the control to be the size of it's parent container.
32316  * In other words width: 100% and height: 100%.
32317  *
32318  * @-x-less FitLayout.less
32319  * @class tinymce.ui.FitLayout
32320  * @extends tinymce.ui.AbsoluteLayout
32321  */
32322 define("tinymce/ui/FitLayout", [
32323 	"tinymce/ui/AbsoluteLayout"
32324 ], function(AbsoluteLayout) {
32325 	"use strict";
32326 
32327 	return AbsoluteLayout.extend({
32328 		/**
32329 		 * Recalculates the positions of the controls in the specified container.
32330 		 *
32331 		 * @method recalc
32332 		 * @param {tinymce.ui.Container} container Container instance to recalc.
32333 		 */
32334 		recalc: function(container) {
32335 			var contLayoutRect = container.layoutRect(), paddingBox = container.paddingBox();
32336 
32337 			container.items().filter(':visible').each(function(ctrl) {
32338 				ctrl.layoutRect({
32339 					x: paddingBox.left,
32340 					y: paddingBox.top,
32341 					w: contLayoutRect.innerW - paddingBox.right - paddingBox.left,
32342 					h: contLayoutRect.innerH - paddingBox.top - paddingBox.bottom
32343 				});
32344 
32345 				if (ctrl.recalc) {
32346 					ctrl.recalc();
32347 				}
32348 			});
32349 		}
32350 	});
32351 });
32352 
32353 // Included from: js/tinymce/classes/ui/FlexLayout.js
32354 
32355 /**
32356  * FlexLayout.js
32357  *
32358  * Copyright, Moxiecode Systems AB
32359  * Released under LGPL License.
32360  *
32361  * License: http://www.tinymce.com/license
32362  * Contributing: http://www.tinymce.com/contributing
32363  */
32364 
32365 /**
32366  * This layout manager works similar to the CSS flex box.
32367  *
32368  * @setting {String} direction row|row-reverse|column|column-reverse
32369  * @setting {Number} flex A positive-number to flex by.
32370  * @setting {String} align start|end|center|stretch
32371  * @setting {String} pack start|end|justify
32372  *
32373  * @class tinymce.ui.FlexLayout
32374  * @extends tinymce.ui.AbsoluteLayout
32375  */
32376 define("tinymce/ui/FlexLayout", [
32377 	"tinymce/ui/AbsoluteLayout"
32378 ], function(AbsoluteLayout) {
32379 	"use strict";
32380 
32381 	return AbsoluteLayout.extend({
32382 		/**
32383 		 * Recalculates the positions of the controls in the specified container.
32384 		 *
32385 		 * @method recalc
32386 		 * @param {tinymce.ui.Container} container Container instance to recalc.
32387 		 */
32388 		recalc: function(container) {
32389 			// A ton of variables, needs to be in the same scope for performance
32390 			var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction;
32391 			var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], size, maxSize, ratio, rect, pos, maxAlignEndPos;
32392 			var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, deltaSizeName, contentSizeName;
32393 			var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignBeforeName, alignAfterName;
32394 			var alignDeltaSizeName, alignContentSizeName;
32395 			var max = Math.max, min = Math.min;
32396 
32397 			// Get container items, properties and settings
32398 			items = container.items().filter(':visible');
32399 			contLayoutRect = container.layoutRect();
32400 			contPaddingBox = container._paddingBox;
32401 			contSettings = container.settings;
32402 			direction = container.isRtl() ? (contSettings.direction || 'row-reversed') : contSettings.direction;
32403 			align = contSettings.align;
32404 			pack = container.isRtl() ? (contSettings.pack || 'end') : contSettings.pack;
32405 			spacing = contSettings.spacing || 0;
32406 
32407 			if (direction == "row-reversed" || direction == "column-reverse") {
32408 				items = items.set(items.toArray().reverse());
32409 				direction = direction.split('-')[0];
32410 			}
32411 
32412 			// Setup axis variable name for row/column direction since the calculations is the same
32413 			if (direction == "column") {
32414 				posName = "y";
32415 				sizeName = "h";
32416 				minSizeName = "minH";
32417 				maxSizeName = "maxH";
32418 				innerSizeName = "innerH";
32419 				beforeName = 'top';
32420 				deltaSizeName = "deltaH";
32421 				contentSizeName = "contentH";
32422 
32423 				alignBeforeName = "left";
32424 				alignSizeName = "w";
32425 				alignAxisName = "x";
32426 				alignInnerSizeName = "innerW";
32427 				alignMinSizeName = "minW";
32428 				alignAfterName = "right";
32429 				alignDeltaSizeName = "deltaW";
32430 				alignContentSizeName = "contentW";
32431 			} else {
32432 				posName = "x";
32433 				sizeName = "w";
32434 				minSizeName = "minW";
32435 				maxSizeName = "maxW";
32436 				innerSizeName = "innerW";
32437 				beforeName = 'left';
32438 				deltaSizeName = "deltaW";
32439 				contentSizeName = "contentW";
32440 
32441 				alignBeforeName = "top";
32442 				alignSizeName = "h";
32443 				alignAxisName = "y";
32444 				alignInnerSizeName = "innerH";
32445 				alignMinSizeName = "minH";
32446 				alignAfterName = "bottom";
32447 				alignDeltaSizeName = "deltaH";
32448 				alignContentSizeName = "contentH";
32449 			}
32450 
32451 			// Figure out total flex, availableSpace and collect any max size elements
32452 			availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName];
32453 			maxAlignEndPos = totalFlex = 0;
32454 			for (i = 0, l = items.length; i < l; i++) {
32455 				ctrl = items[i];
32456 				ctrlLayoutRect = ctrl.layoutRect();
32457 				ctrlSettings = ctrl.settings;
32458 				flex = ctrlSettings.flex;
32459 				availableSpace -= (i < l - 1 ? spacing : 0);
32460 
32461 				if (flex > 0) {
32462 					totalFlex += flex;
32463 
32464 					// Flexed item has a max size then we need to check if we will hit that size
32465 					if (ctrlLayoutRect[maxSizeName]) {
32466 						maxSizeItems.push(ctrl);
32467 					}
32468 
32469 					ctrlLayoutRect.flex = flex;
32470 				}
32471 
32472 				availableSpace -= ctrlLayoutRect[minSizeName];
32473 
32474 				// Calculate the align end position to be used to check for overflow/underflow
32475 				size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName];
32476 				if (size > maxAlignEndPos) {
32477 					maxAlignEndPos = size;
32478 				}
32479 			}
32480 
32481 			// Calculate minW/minH
32482 			rect = {};
32483 			if (availableSpace < 0) {
32484 				rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName];
32485 			} else {
32486 				rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName];
32487 			}
32488 
32489 			rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName];
32490 
32491 			rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace;
32492 			rect[alignContentSizeName] = maxAlignEndPos;
32493 			rect.minW = min(rect.minW, contLayoutRect.maxW);
32494 			rect.minH = min(rect.minH, contLayoutRect.maxH);
32495 			rect.minW = max(rect.minW, contLayoutRect.startMinWidth);
32496 			rect.minH = max(rect.minH, contLayoutRect.startMinHeight);
32497 
32498 			// Resize container container if minSize was changed
32499 			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
32500 				rect.w = rect.minW;
32501 				rect.h = rect.minH;
32502 
32503 				container.layoutRect(rect);
32504 				this.recalc(container);
32505 
32506 				// Forced recalc for example if items are hidden/shown
32507 				if (container._lastRect === null) {
32508 					var parentCtrl = container.parent();
32509 					if (parentCtrl) {
32510 						parentCtrl._lastRect = null;
32511 						parentCtrl.recalc();
32512 					}
32513 				}
32514 
32515 				return;
32516 			}
32517 
32518 			// Handle max size elements, check if they will become to wide with current options
32519 			ratio = availableSpace / totalFlex;
32520 			for (i = 0, l = maxSizeItems.length; i < l; i++) {
32521 				ctrl = maxSizeItems[i];
32522 				ctrlLayoutRect = ctrl.layoutRect();
32523 				maxSize = ctrlLayoutRect[maxSizeName];
32524 				size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio;
32525 
32526 				if (size > maxSize) {
32527 					availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]);
32528 					totalFlex -= ctrlLayoutRect.flex;
32529 					ctrlLayoutRect.flex = 0;
32530 					ctrlLayoutRect.maxFlexSize = maxSize;
32531 				} else {
32532 					ctrlLayoutRect.maxFlexSize = 0;
32533 				}
32534 			}
32535 
32536 			// Setup new ratio, target layout rect, start position
32537 			ratio = availableSpace / totalFlex;
32538 			pos = contPaddingBox[beforeName];
32539 			rect = {};
32540 
32541 			// Handle pack setting moves the start position to end, center
32542 			if (totalFlex === 0) {
32543 				if (pack == "end") {
32544 					pos = availableSpace + contPaddingBox[beforeName];
32545 				} else if (pack == "center") {
32546 					pos = Math.round(
32547 						(contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2)
32548 					) + contPaddingBox[beforeName];
32549 
32550 					if (pos < 0) {
32551 						pos = contPaddingBox[beforeName];
32552 					}
32553 				} else if (pack == "justify") {
32554 					pos = contPaddingBox[beforeName];
32555 					spacing = Math.floor(availableSpace / (items.length - 1));
32556 				}
32557 			}
32558 
32559 			// Default aligning (start) the other ones needs to be calculated while doing the layout
32560 			rect[alignAxisName] = contPaddingBox[alignBeforeName];
32561 
32562 			// Start laying out controls
32563 			for (i = 0, l = items.length; i < l; i++) {
32564 				ctrl = items[i];
32565 				ctrlLayoutRect = ctrl.layoutRect();
32566 				size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName];
32567 
32568 				// Align the control on the other axis
32569 				if (align === "center") {
32570 					rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2));
32571 				} else if (align === "stretch") {
32572 					rect[alignSizeName] = max(
32573 						ctrlLayoutRect[alignMinSizeName] || 0,
32574 						contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName]
32575 					);
32576 					rect[alignAxisName] = contPaddingBox[alignBeforeName];
32577 				} else if (align === "end") {
32578 					rect[alignAxisName] = contLayoutRect[alignInnerSizeName]  - ctrlLayoutRect[alignSizeName]  - contPaddingBox.top;
32579 				}
32580 
32581 				// Calculate new size based on flex
32582 				if (ctrlLayoutRect.flex > 0) {
32583 					size += ctrlLayoutRect.flex * ratio;
32584 				}
32585 
32586 				rect[sizeName] = size;
32587 				rect[posName] = pos;
32588 				ctrl.layoutRect(rect);
32589 
32590 				// Recalculate containers
32591 				if (ctrl.recalc) {
32592 					ctrl.recalc();
32593 				}
32594 
32595 				// Move x/y position
32596 				pos += size + spacing;
32597 			}
32598 		}
32599 	});
32600 });
32601 
32602 // Included from: js/tinymce/classes/ui/FlowLayout.js
32603 
32604 /**
32605  * FlowLayout.js
32606  *
32607  * Copyright, Moxiecode Systems AB
32608  * Released under LGPL License.
32609  *
32610  * License: http://www.tinymce.com/license
32611  * Contributing: http://www.tinymce.com/contributing
32612  */
32613 
32614 /**
32615  * This layout manager will place the controls by using the browsers native layout.
32616  *
32617  * @-x-less FlowLayout.less
32618  * @class tinymce.ui.FlowLayout
32619  * @extends tinymce.ui.Layout
32620  */
32621 define("tinymce/ui/FlowLayout", [
32622 	"tinymce/ui/Layout"
32623 ], function(Layout) {
32624 	return Layout.extend({
32625 		Defaults: {
32626 			containerClass: 'flow-layout',
32627 			controlClass: 'flow-layout-item',
32628 			endClass : 'break'
32629 		},
32630 
32631 		/**
32632 		 * Recalculates the positions of the controls in the specified container.
32633 		 *
32634 		 * @method recalc
32635 		 * @param {tinymce.ui.Container} container Container instance to recalc.
32636 		 */
32637 		recalc: function(container) {
32638 			container.items().filter(':visible').each(function(ctrl) {
32639 				if (ctrl.recalc) {
32640 					ctrl.recalc();
32641 				}
32642 			});
32643 		}
32644 	});
32645 });
32646 
32647 // Included from: js/tinymce/classes/ui/FormatControls.js
32648 
32649 /**
32650  * FormatControls.js
32651  *
32652  * Copyright, Moxiecode Systems AB
32653  * Released under LGPL License.
32654  *
32655  * License: http://www.tinymce.com/license
32656  * Contributing: http://www.tinymce.com/contributing
32657  */
32658 
32659 /**
32660  * Internal class containing all TinyMCE specific control types such as
32661  * format listboxes, fontlist boxes, toolbar buttons etc.
32662  *
32663  * @class tinymce.ui.FormatControls
32664  */
32665 define("tinymce/ui/FormatControls", [
32666 	"tinymce/ui/Control",
32667 	"tinymce/ui/Widget",
32668 	"tinymce/ui/FloatPanel",
32669 	"tinymce/util/Tools",
32670 	"tinymce/EditorManager",
32671 	"tinymce/Env"
32672 ], function(Control, Widget, FloatPanel, Tools, EditorManager, Env) {
32673 	var each = Tools.each;
32674 
32675 	EditorManager.on('AddEditor', function(e) {
32676 		if (e.editor.rtl) {
32677 			Control.rtl = true;
32678 		}
32679 
32680 		registerControls(e.editor);
32681 	});
32682 
32683 	Control.translate = function(text) {
32684 		return EditorManager.translate(text);
32685 	};
32686 
32687 	Widget.tooltips = !Env.iOS;
32688 
32689 	function registerControls(editor) {
32690 		var formatMenu;
32691 
32692 		function createListBoxChangeHandler(items, formatName) {
32693 			return function() {
32694 				var self = this;
32695 
32696 				editor.on('nodeChange', function(e) {
32697 					var formatter = editor.formatter;
32698 					var value = null;
32699 
32700 					each(e.parents, function(node) {
32701 						each(items, function(item) {
32702 							if (formatName) {
32703 								if (formatter.matchNode(node, formatName, {value: item.value})) {
32704 									value = item.value;
32705 								}
32706 							} else {
32707 								if (formatter.matchNode(node, item.value)) {
32708 									value = item.value;
32709 								}
32710 							}
32711 
32712 							if (value) {
32713 								return false;
32714 							}
32715 						});
32716 
32717 						if (value) {
32718 							return false;
32719 						}
32720 					});
32721 
32722 					self.value(value);
32723 				});
32724 			};
32725 		}
32726 
32727 		function createFormats(formats) {
32728 			formats = formats.replace(/;$/, '').split(';');
32729 
32730 			var i = formats.length;
32731 			while (i--) {
32732 				formats[i] = formats[i].split('=');
32733 			}
32734 
32735 			return formats;
32736 		}
32737 
32738 		function createFormatMenu() {
32739 			var count = 0, newFormats = [];
32740 
32741 			var defaultStyleFormats = [
32742 				{title: 'Headings', items: [
32743 					{title: 'Heading 1', format: 'h1'},
32744 					{title: 'Heading 2', format: 'h2'},
32745 					{title: 'Heading 3', format: 'h3'},
32746 					{title: 'Heading 4', format: 'h4'},
32747 					{title: 'Heading 5', format: 'h5'},
32748 					{title: 'Heading 6', format: 'h6'}
32749 				]},
32750 
32751 				{title: 'Inline', items: [
32752 					{title: 'Bold', icon: 'bold', format: 'bold'},
32753 					{title: 'Italic', icon: 'italic', format: 'italic'},
32754 					{title: 'Underline', icon: 'underline', format: 'underline'},
32755 					{title: 'Strikethrough', icon: 'strikethrough', format: 'strikethrough'},
32756 					{title: 'Superscript', icon: 'superscript', format: 'superscript'},
32757 					{title: 'Subscript', icon: 'subscript', format: 'subscript'},
32758 					{title: 'Code', icon: 'code', format: 'code'}
32759 				]},
32760 
32761 				{title: 'Blocks', items: [
32762 					{title: 'Paragraph', format: 'p'},
32763 					{title: 'Blockquote', format: 'blockquote'},
32764 					{title: 'Div', format: 'div'},
32765 					{title: 'Pre', format: 'pre'}
32766 				]},
32767 
32768 				{title: 'Alignment', items: [
32769 					{title: 'Left', icon: 'alignleft', format: 'alignleft'},
32770 					{title: 'Center', icon: 'aligncenter', format: 'aligncenter'},
32771 					{title: 'Right', icon: 'alignright', format: 'alignright'},
32772 					{title: 'Justify', icon: 'alignjustify', format: 'alignjustify'}
32773 				]}
32774 			];
32775 
32776 			function createMenu(formats) {
32777 				var menu = [];
32778 
32779 				if (!formats) {
32780 					return;
32781 				}
32782 
32783 				each(formats, function(format) {
32784 					var menuItem = {
32785 						text: format.title,
32786 						icon: format.icon
32787 					};
32788 
32789 					if (format.items) {
32790 						menuItem.menu = createMenu(format.items);
32791 					} else {
32792 						var formatName = format.format || "custom" + count++;
32793 
32794 						if (!format.format) {
32795 							format.name = formatName;
32796 							newFormats.push(format);
32797 						}
32798 
32799 						menuItem.format = formatName;
32800 					}
32801 
32802 					menu.push(menuItem);
32803 				});
32804 
32805 				return menu;
32806 			}
32807 
32808 			function createStylesMenu() {
32809 				var menu;
32810 
32811 				if (editor.settings.style_formats_merge) {
32812 					if (editor.settings.style_formats) {
32813 						menu = createMenu(defaultStyleFormats.concat(editor.settings.style_formats));
32814 					} else {
32815 						menu = createMenu(defaultStyleFormats);
32816 					}
32817 				} else {
32818 					menu = createMenu(editor.settings.style_formats || defaultStyleFormats);
32819 				}
32820 
32821 				return menu;
32822 			}
32823 
32824 			editor.on('init', function() {
32825 				each(newFormats, function(format) {
32826 					editor.formatter.register(format.name, format);
32827 				});
32828 			});
32829 
32830 			return {
32831 				type: 'menu',
32832 				items: createStylesMenu(),
32833 				onPostRender: function(e) {
32834 					editor.fire('renderFormatsMenu', {control: e.control});
32835 				},
32836 				itemDefaults: {
32837 					preview: true,
32838 
32839 					textStyle: function() {
32840 						if (this.settings.format) {
32841 							return editor.formatter.getCssText(this.settings.format);
32842 						}
32843 					},
32844 
32845 					onPostRender: function() {
32846 						var self = this, formatName = this.settings.format;
32847 
32848 						if (formatName) {
32849 							self.parent().on('show', function() {
32850 								self.disabled(!editor.formatter.canApply(formatName));
32851 								self.active(editor.formatter.match(formatName));
32852 							});
32853 						}
32854 					},
32855 
32856 					onclick: function() {
32857 						if (this.settings.format) {
32858 							toggleFormat(this.settings.format);
32859 						}
32860 					}
32861 				}
32862 			};
32863 		}
32864 
32865 		formatMenu = createFormatMenu();
32866 
32867 		// Simple format controls <control/format>:<UI text>
32868 		each({
32869 			bold: 'Bold',
32870 			italic: 'Italic',
32871 			underline: 'Underline',
32872 			strikethrough: 'Strikethrough',
32873 			subscript: 'Subscript',
32874 			superscript: 'Superscript'
32875 		}, function(text, name) {
32876 			editor.addButton(name, {
32877 				tooltip: text,
32878 				onPostRender: function() {
32879 					var self = this;
32880 
32881 					// TODO: Fix this
32882 					if (editor.formatter) {
32883 						editor.formatter.formatChanged(name, function(state) {
32884 							self.active(state);
32885 						});
32886 					} else {
32887 						editor.on('init', function() {
32888 							editor.formatter.formatChanged(name, function(state) {
32889 								self.active(state);
32890 							});
32891 						});
32892 					}
32893 				},
32894 				onclick: function() {
32895 					toggleFormat(name);
32896 				}
32897 			});
32898 		});
32899 
32900 		// Simple command controls <control>:[<UI text>,<Command>]
32901 		each({
32902 			outdent: ['Decrease indent', 'Outdent'],
32903 			indent: ['Increase indent', 'Indent'],
32904 			cut: ['Cut', 'Cut'],
32905 			copy: ['Copy', 'Copy'],
32906 			paste: ['Paste', 'Paste'],
32907 			help: ['Help', 'mceHelp'],
32908 			selectall: ['Select all', 'SelectAll'],
32909 			hr: ['Insert horizontal rule', 'InsertHorizontalRule'],
32910 			removeformat: ['Clear formatting', 'RemoveFormat'],
32911 			visualaid: ['Visual aids', 'mceToggleVisualAid'],
32912 			newdocument: ['New document', 'mceNewDocument']
32913 		}, function(item, name) {
32914 			editor.addButton(name, {
32915 				tooltip: item[0],
32916 				cmd: item[1]
32917 			});
32918 		});
32919 
32920 		// Simple command controls with format state
32921 		each({
32922 			blockquote: ['Blockquote', 'mceBlockQuote'],
32923 			numlist: ['Numbered list', 'InsertOrderedList'],
32924 			bullist: ['Bullet list', 'InsertUnorderedList'],
32925 			subscript: ['Subscript', 'Subscript'],
32926 			superscript: ['Superscript', 'Superscript'],
32927 			alignleft: ['Align left', 'JustifyLeft'],
32928 			aligncenter: ['Align center', 'JustifyCenter'],
32929 			alignright: ['Align right', 'JustifyRight'],
32930 			alignjustify: ['Justify', 'JustifyFull']
32931 		}, function(item, name) {
32932 			editor.addButton(name, {
32933 				tooltip: item[0],
32934 				cmd: item[1],
32935 				onPostRender: function() {
32936 					var self = this;
32937 
32938 					// TODO: Fix this
32939 					if (editor.formatter) {
32940 						editor.formatter.formatChanged(name, function(state) {
32941 							self.active(state);
32942 						});
32943 					} else {
32944 						editor.on('init', function() {
32945 							editor.formatter.formatChanged(name, function(state) {
32946 								self.active(state);
32947 							});
32948 						});
32949 					}
32950 				}
32951 			});
32952 		});
32953 
32954 		function hasUndo() {
32955 			return editor.undoManager ? editor.undoManager.hasUndo() : false;
32956 		}
32957 
32958 		function hasRedo() {
32959 			return editor.undoManager ? editor.undoManager.hasRedo() : false;
32960 		}
32961 
32962 		function toggleUndoState() {
32963 			var self = this;
32964 
32965 			self.disabled(!hasUndo());
32966 			editor.on('Undo Redo AddUndo TypingUndo', function() {
32967 				self.disabled(!hasUndo());
32968 			});
32969 		}
32970 
32971 		function toggleRedoState() {
32972 			var self = this;
32973 
32974 			self.disabled(!hasRedo());
32975 			editor.on('Undo Redo AddUndo TypingUndo', function() {
32976 				self.disabled(!hasRedo());
32977 			});
32978 		}
32979 
32980 		function toggleVisualAidState() {
32981 			var self = this;
32982 
32983 			editor.on('VisualAid', function(e) {
32984 				self.active(e.hasVisual);
32985 			});
32986 
32987 			self.active(editor.hasVisual);
32988 		}
32989 
32990 		editor.addButton('undo', {
32991 			tooltip: 'Undo',
32992 			onPostRender: toggleUndoState,
32993 			cmd: 'undo'
32994 		});
32995 
32996 		editor.addButton('redo', {
32997 			tooltip: 'Redo',
32998 			onPostRender: toggleRedoState,
32999 			cmd: 'redo'
33000 		});
33001 
33002 		editor.addMenuItem('newdocument', {
33003 			text: 'New document',
33004 			shortcut: 'Ctrl+N',
33005 			icon: 'newdocument',
33006 			cmd: 'mceNewDocument'
33007 		});
33008 
33009 		editor.addMenuItem('undo', {
33010 			text: 'Undo',
33011 			icon: 'undo',
33012 			shortcut: 'Ctrl+Z',
33013 			onPostRender: toggleUndoState,
33014 			cmd: 'undo'
33015 		});
33016 
33017 		editor.addMenuItem('redo', {
33018 			text: 'Redo',
33019 			icon: 'redo',
33020 			shortcut: 'Ctrl+Y',
33021 			onPostRender: toggleRedoState,
33022 			cmd: 'redo'
33023 		});
33024 
33025 		editor.addMenuItem('visualaid', {
33026 			text: 'Visual aids',
33027 			selectable: true,
33028 			onPostRender: toggleVisualAidState,
33029 			cmd: 'mceToggleVisualAid'
33030 		});
33031 
33032 		each({
33033 			cut: ['Cut', 'Cut', 'Ctrl+X'],
33034 			copy: ['Copy', 'Copy', 'Ctrl+C'],
33035 			paste: ['Paste', 'Paste', 'Ctrl+V'],
33036 			selectall: ['Select all', 'SelectAll', 'Ctrl+A'],
33037 			bold: ['Bold', 'Bold', 'Ctrl+B'],
33038 			italic: ['Italic', 'Italic', 'Ctrl+I'],
33039 			underline: ['Underline', 'Underline'],
33040 			strikethrough: ['Strikethrough', 'Strikethrough'],
33041 			subscript: ['Subscript', 'Subscript'],
33042 			superscript: ['Superscript', 'Superscript'],
33043 			removeformat: ['Clear formatting', 'RemoveFormat']
33044 		}, function(item, name) {
33045 			editor.addMenuItem(name, {
33046 				text: item[0],
33047 				icon: name,
33048 				shortcut: item[2],
33049 				cmd: item[1]
33050 			});
33051 		});
33052 
33053 		editor.on('mousedown', function() {
33054 			FloatPanel.hideAll();
33055 		});
33056 
33057 		function toggleFormat(fmt) {
33058 			if (fmt.control) {
33059 				fmt = fmt.control.value();
33060 			}
33061 
33062 			if (fmt) {
33063 				editor.execCommand('mceToggleFormat', false, fmt);
33064 			}
33065 		}
33066 
33067 		editor.addButton('styleselect', {
33068 			type: 'menubutton',
33069 			text: 'Formats',
33070 			menu: formatMenu
33071 		});
33072 
33073 		editor.addButton('formatselect', function() {
33074 			var items = [], blocks = createFormats(editor.settings.block_formats ||
33075 				'Paragraph=p;' +
33076 				'Address=address;' +
33077 				'Pre=pre;' +
33078 				'Heading 1=h1;' +
33079 				'Heading 2=h2;' +
33080 				'Heading 3=h3;' +
33081 				'Heading 4=h4;' +
33082 				'Heading 5=h5;' +
33083 				'Heading 6=h6'
33084 			);
33085 
33086 			each(blocks, function(block) {
33087 				items.push({
33088 					text: block[0],
33089 					value: block[1],
33090 					textStyle: function() {
33091 						return editor.formatter.getCssText(block[1]);
33092 					}
33093 				});
33094 			});
33095 
33096 			return {
33097 				type: 'listbox',
33098 				text: blocks[0][0],
33099 				values: items,
33100 				fixedWidth: true,
33101 				onselect: toggleFormat,
33102 				onPostRender: createListBoxChangeHandler(items)
33103 			};
33104 		});
33105 
33106 		editor.addButton('fontselect', function() {
33107 			var defaultFontsFormats =
33108 				'Andale Mono=andale mono,times;' +
33109 				'Arial=arial,helvetica,sans-serif;' +
33110 				'Arial Black=arial black,avant garde;' +
33111 				'Book Antiqua=book antiqua,palatino;' +
33112 				'Comic Sans MS=comic sans ms,sans-serif;' +
33113 				'Courier New=courier new,courier;' +
33114 				'Georgia=georgia,palatino;' +
33115 				'Helvetica=helvetica;' +
33116 				'Impact=impact,chicago;' +
33117 				'Symbol=symbol;' +
33118 				'Tahoma=tahoma,arial,helvetica,sans-serif;' +
33119 				'Terminal=terminal,monaco;' +
33120 				'Times New Roman=times new roman,times;' +
33121 				'Trebuchet MS=trebuchet ms,geneva;' +
33122 				'Verdana=verdana,geneva;' +
33123 				'Webdings=webdings;' +
33124 				'Wingdings=wingdings,zapf dingbats';
33125 
33126 			var items = [], fonts = createFormats(editor.settings.font_formats || defaultFontsFormats);
33127 
33128 			each(fonts, function(font) {
33129 				items.push({
33130 					text: {raw: font[0]},
33131 					value: font[1],
33132 					textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : ''
33133 				});
33134 			});
33135 
33136 			return {
33137 				type: 'listbox',
33138 				text: 'Font Family',
33139 				tooltip: 'Font Family',
33140 				values: items,
33141 				fixedWidth: true,
33142 				onPostRender: createListBoxChangeHandler(items, 'fontname'),
33143 				onselect: function(e) {
33144 					if (e.control.settings.value) {
33145 						editor.execCommand('FontName', false, e.control.settings.value);
33146 					}
33147 				}
33148 			};
33149 		});
33150 
33151 		editor.addButton('fontsizeselect', function() {
33152 			var items = [], defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt';
33153 			var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
33154 
33155 			each(fontsize_formats.split(' '), function(item) {
33156 				items.push({text: item, value: item});
33157 			});
33158 
33159 			return {
33160 				type: 'listbox',
33161 				text: 'Font Sizes',
33162 				tooltip: 'Font Sizes',
33163 				values: items,
33164 				fixedWidth: true,
33165 				onPostRender: createListBoxChangeHandler(items, 'fontsize'),
33166 				onclick: function(e) {
33167 					if (e.control.settings.value) {
33168 						editor.execCommand('FontSize', false, e.control.settings.value);
33169 					}
33170 				}
33171 			};
33172 		});
33173 
33174 		editor.addMenuItem('formats', {
33175 			text: 'Formats',
33176 			menu: formatMenu
33177 		});
33178 	}
33179 });
33180 
33181 // Included from: js/tinymce/classes/ui/GridLayout.js
33182 
33183 /**
33184  * GridLayout.js
33185  *
33186  * Copyright, Moxiecode Systems AB
33187  * Released under LGPL License.
33188  *
33189  * License: http://www.tinymce.com/license
33190  * Contributing: http://www.tinymce.com/contributing
33191  */
33192 
33193 /**
33194  * This layout manager places controls in a grid.
33195  *
33196  * @setting {Number} spacing Spacing between controls.
33197  * @setting {Number} spacingH Horizontal spacing between controls.
33198  * @setting {Number} spacingV Vertical spacing between controls.
33199  * @setting {Number} columns Number of columns to use.
33200  * @setting {String/Array} alignH start|end|center|stretch or array of values for each column.
33201  * @setting {String/Array} alignV start|end|center|stretch or array of values for each column.
33202  * @setting {String} pack start|end
33203  *
33204  * @class tinymce.ui.GridLayout
33205  * @extends tinymce.ui.AbsoluteLayout
33206  */
33207 define("tinymce/ui/GridLayout", [
33208 	"tinymce/ui/AbsoluteLayout"
33209 ], function(AbsoluteLayout) {
33210 	"use strict";
33211 
33212 	return AbsoluteLayout.extend({
33213 		/**
33214 		 * Recalculates the positions of the controls in the specified container.
33215 		 *
33216 		 * @method recalc
33217 		 * @param {tinymce.ui.Container} container Container instance to recalc.
33218 		 */
33219 		recalc: function(container) {
33220 			var settings = container.settings, rows, cols, items, contLayoutRect, width, height, rect,
33221 				ctrlLayoutRect, ctrl, x, y, posX, posY, ctrlSettings, contPaddingBox, align, spacingH, spacingV, alignH, alignV, maxX, maxY,
33222 				colWidths = [], rowHeights = [], ctrlMinWidth, ctrlMinHeight, availableWidth, availableHeight;
33223 
33224 			// Get layout settings
33225 			settings = container.settings;
33226 			items = container.items().filter(':visible');
33227 			contLayoutRect = container.layoutRect();
33228 			cols = settings.columns || Math.ceil(Math.sqrt(items.length));
33229 			rows = Math.ceil(items.length / cols);
33230 			spacingH = settings.spacingH || settings.spacing || 0;
33231 			spacingV = settings.spacingV || settings.spacing || 0;
33232 			alignH = settings.alignH || settings.align;
33233 			alignV = settings.alignV || settings.align;
33234 			contPaddingBox = container._paddingBox;
33235 
33236 			if (alignH && typeof(alignH) == "string") {
33237 				alignH = [alignH];
33238 			}
33239 
33240 			if (alignV && typeof(alignV) == "string") {
33241 				alignV = [alignV];
33242 			}
33243 
33244 			// Zero padd columnWidths
33245 			for (x = 0; x < cols; x++) {
33246 				colWidths.push(0);
33247 			}
33248 
33249 			// Zero padd rowHeights
33250 			for (y = 0; y < rows; y++) {
33251 				rowHeights.push(0);
33252 			}
33253 
33254 			// Calculate columnWidths and rowHeights
33255 			for (y = 0; y < rows; y++) {
33256 				for (x = 0; x < cols; x++) {
33257 					ctrl = items[y * cols + x];
33258 
33259 					// Out of bounds
33260 					if (!ctrl) {
33261 						break;
33262 					}
33263 
33264 					ctrlLayoutRect = ctrl.layoutRect();
33265 					ctrlMinWidth = ctrlLayoutRect.minW;
33266 					ctrlMinHeight = ctrlLayoutRect.minH;
33267 
33268 					colWidths[x] = ctrlMinWidth > colWidths[x] ? ctrlMinWidth : colWidths[x];
33269 					rowHeights[y] = ctrlMinHeight > rowHeights[y] ? ctrlMinHeight : rowHeights[y];
33270 				}
33271 			}
33272 
33273 			// Calculate maxX
33274 			availableWidth = contLayoutRect.innerW - contPaddingBox.left - contPaddingBox.right;
33275 			for (maxX = 0, x = 0; x < cols; x++) {
33276 				maxX += colWidths[x] + (x > 0 ? spacingH : 0);
33277 				availableWidth -= (x > 0 ? spacingH : 0) + colWidths[x];
33278 			}
33279 
33280 			// Calculate maxY
33281 			availableHeight = contLayoutRect.innerH - contPaddingBox.top - contPaddingBox.bottom;
33282 			for (maxY = 0, y = 0; y < rows; y++) {
33283 				maxY += rowHeights[y] + (y > 0 ? spacingV : 0);
33284 				availableHeight -= (y > 0 ? spacingV : 0) + rowHeights[y];
33285 			}
33286 
33287 			maxX += contPaddingBox.left + contPaddingBox.right;
33288 			maxY += contPaddingBox.top + contPaddingBox.bottom;
33289 
33290 			// Calculate minW/minH
33291 			rect = {};
33292 			rect.minW = maxX + (contLayoutRect.w - contLayoutRect.innerW);
33293 			rect.minH = maxY + (contLayoutRect.h - contLayoutRect.innerH);
33294 
33295 			rect.contentW = rect.minW - contLayoutRect.deltaW;
33296 			rect.contentH = rect.minH - contLayoutRect.deltaH;
33297 			rect.minW = Math.min(rect.minW, contLayoutRect.maxW);
33298 			rect.minH = Math.min(rect.minH, contLayoutRect.maxH);
33299 			rect.minW = Math.max(rect.minW, contLayoutRect.startMinWidth);
33300 			rect.minH = Math.max(rect.minH, contLayoutRect.startMinHeight);
33301 
33302 			// Resize container container if minSize was changed
33303 			if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) {
33304 				rect.w = rect.minW;
33305 				rect.h = rect.minH;
33306 
33307 				container.layoutRect(rect);
33308 				this.recalc(container);
33309 
33310 				// Forced recalc for example if items are hidden/shown
33311 				if (container._lastRect === null) {
33312 					var parentCtrl = container.parent();
33313 					if (parentCtrl) {
33314 						parentCtrl._lastRect = null;
33315 						parentCtrl.recalc();
33316 					}
33317 				}
33318 
33319 				return;
33320 			}
33321 
33322 			// Update contentW/contentH so absEnd moves correctly
33323 			if (contLayoutRect.autoResize) {
33324 				rect = container.layoutRect(rect);
33325 				rect.contentW = rect.minW - contLayoutRect.deltaW;
33326 				rect.contentH = rect.minH - contLayoutRect.deltaH;
33327 			}
33328 
33329 			var flexV;
33330 
33331 			if (settings.packV == 'start') {
33332 				flexV = 0;
33333 			} else {
33334 				flexV = availableHeight > 0 ? Math.floor(availableHeight / rows) : 0;
33335 			}
33336 
33337 			// Calculate totalFlex
33338 			var totalFlex = 0;
33339 			var flexWidths = settings.flexWidths;
33340 			if (flexWidths) {
33341 				for (x = 0; x < flexWidths.length; x++) {
33342 					totalFlex += flexWidths[x];
33343 				}
33344 			} else {
33345 				totalFlex = cols;
33346 			}
33347 
33348 			// Calculate new column widths based on flex values
33349 			var ratio = availableWidth / totalFlex;
33350 			for (x = 0; x < cols; x++) {
33351 				colWidths[x] += flexWidths ? flexWidths[x] * ratio : ratio;
33352 			}
33353 
33354 			// Move/resize controls
33355 			posY = contPaddingBox.top;
33356 			for (y = 0; y < rows; y++) {
33357 				posX = contPaddingBox.left;
33358 				height = rowHeights[y] + flexV;
33359 
33360 				for (x = 0; x < cols; x++) {
33361 					ctrl = items[y * cols + x];
33362 
33363 					// No more controls to render then break
33364 					if (!ctrl) {
33365 						break;
33366 					}
33367 
33368 					// Get control settings and calculate x, y
33369 					ctrlSettings = ctrl.settings;
33370 					ctrlLayoutRect = ctrl.layoutRect();
33371 					width = Math.max(colWidths[x], ctrlLayoutRect.startMinWidth);
33372 					ctrlLayoutRect.x = posX;
33373 					ctrlLayoutRect.y = posY;
33374 
33375 					// Align control horizontal
33376 					align = ctrlSettings.alignH || (alignH ? (alignH[x] || alignH[0]) : null);
33377 					if (align == "center") {
33378 						ctrlLayoutRect.x = posX + (width / 2) - (ctrlLayoutRect.w / 2);
33379 					} else if (align == "right") {
33380 						ctrlLayoutRect.x = posX + width - ctrlLayoutRect.w;
33381 					} else if (align == "stretch") {
33382 						ctrlLayoutRect.w = width;
33383 					}
33384 
33385 					// Align control vertical
33386 					align = ctrlSettings.alignV || (alignV ? (alignV[x] || alignV[0]) : null);
33387 					if (align == "center") {
33388 						ctrlLayoutRect.y = posY + (height / 2) - (ctrlLayoutRect.h / 2);
33389 					} else  if (align == "bottom") {
33390 						ctrlLayoutRect.y = posY + height - ctrlLayoutRect.h;
33391 					} else if (align == "stretch") {
33392 						ctrlLayoutRect.h = height;
33393 					}
33394 
33395 					ctrl.layoutRect(ctrlLayoutRect);
33396 
33397 					posX += width + spacingH;
33398 
33399 					if (ctrl.recalc) {
33400 						ctrl.recalc();
33401 					}
33402 				}
33403 
33404 				posY += height + spacingV;
33405 			}
33406 		}
33407 	});
33408 });
33409 
33410 // Included from: js/tinymce/classes/ui/Iframe.js
33411 
33412 /**
33413  * Iframe.js
33414  *
33415  * Copyright, Moxiecode Systems AB
33416  * Released under LGPL License.
33417  *
33418  * License: http://www.tinymce.com/license
33419  * Contributing: http://www.tinymce.com/contributing
33420  */
33421 
33422 /*jshint scripturl:true */
33423 
33424 /**
33425  * This class creates an iframe.
33426  *
33427  * @setting {String} url Url to open in the iframe.
33428  *
33429  * @-x-less Iframe.less
33430  * @class tinymce.ui.Iframe
33431  * @extends tinymce.ui.Widget
33432  */
33433 define("tinymce/ui/Iframe", [
33434 	"tinymce/ui/Widget"
33435 ], function(Widget) {
33436 	"use strict";
33437 
33438 	return Widget.extend({
33439 		/**
33440 		 * Renders the control as a HTML string.
33441 		 *
33442 		 * @method renderHtml
33443 		 * @return {String} HTML representing the control.
33444 		 */
33445 		renderHtml: function() {
33446 			var self = this;
33447 
33448 			self.addClass('iframe');
33449 			self.canFocus = false;
33450 
33451 			/*eslint no-script-url:0 */
33452 			return (
33453 				'<iframe id="' + self._id + '" class="' + self.classes() + '" tabindex="-1" src="' +
33454 				(self.settings.url || "javascript:\'\'") + '" frameborder="0"></iframe>'
33455 			);
33456 		},
33457 
33458 		/**
33459 		 * Setter for the iframe source.
33460 		 *
33461 		 * @method src
33462 		 * @param {String} src Source URL for iframe.
33463 		 */
33464 		src: function(src) {
33465 			this.getEl().src = src;
33466 		},
33467 
33468 		/**
33469 		 * Inner HTML for the iframe.
33470 		 *
33471 		 * @method html
33472 		 * @param {String} html HTML string to set as HTML inside the iframe.
33473 		 * @param {function} callback Optional callback to execute when the iframe body is filled with contents.
33474 		 * @return {tinymce.ui.Iframe} Current iframe control.
33475 		 */
33476 		html: function(html, callback) {
33477 			var self = this, body = this.getEl().contentWindow.document.body;
33478 
33479 			// Wait for iframe to initialize IE 10 takes time
33480 			if (!body) {
33481 				setTimeout(function() {
33482 					self.html(html);
33483 				}, 0);
33484 			} else {
33485 				body.innerHTML = html;
33486 
33487 				if (callback) {
33488 					callback();
33489 				}
33490 			}
33491 
33492 			return this;
33493 		}
33494 	});
33495 });
33496 
33497 // Included from: js/tinymce/classes/ui/Label.js
33498 
33499 /**
33500  * Label.js
33501  *
33502  * Copyright, Moxiecode Systems AB
33503  * Released under LGPL License.
33504  *
33505  * License: http://www.tinymce.com/license
33506  * Contributing: http://www.tinymce.com/contributing
33507  */
33508 
33509 /**
33510  * This class creates a label element. A label is a simple text control
33511  * that can be bound to other controls.
33512  *
33513  * @-x-less Label.less
33514  * @class tinymce.ui.Label
33515  * @extends tinymce.ui.Widget
33516  */
33517 define("tinymce/ui/Label", [
33518 	"tinymce/ui/Widget",
33519 	"tinymce/ui/DomUtils"
33520 ], function(Widget, DomUtils) {
33521 	"use strict";
33522 
33523 	return Widget.extend({
33524 		/**
33525 		 * Constructs a instance with the specified settings.
33526 		 *
33527 		 * @constructor
33528 		 * @param {Object} settings Name/value object with settings.
33529 		 * @param {Boolean} multiline Multiline label.
33530 		 */
33531 		init: function(settings) {
33532 			var self = this;
33533 
33534 			self._super(settings);
33535 			self.addClass('widget');
33536 			self.addClass('label');
33537 			self.canFocus = false;
33538 
33539 			if (settings.multiline) {
33540 				self.addClass('autoscroll');
33541 			}
33542 
33543 			if (settings.strong) {
33544 				self.addClass('strong');
33545 			}
33546 		},
33547 
33548 		/**
33549 		 * Initializes the current controls layout rect.
33550 		 * This will be executed by the layout managers to determine the
33551 		 * default minWidth/minHeight etc.
33552 		 *
33553 		 * @method initLayoutRect
33554 		 * @return {Object} Layout rect instance.
33555 		 */
33556 		initLayoutRect: function() {
33557 			var self = this, layoutRect = self._super();
33558 
33559 			if (self.settings.multiline) {
33560 				var size = DomUtils.getSize(self.getEl());
33561 
33562 				// Check if the text fits within maxW if not then try word wrapping it
33563 				if (size.width > layoutRect.maxW) {
33564 					layoutRect.minW = layoutRect.maxW;
33565 					self.addClass('multiline');
33566 				}
33567 
33568 				self.getEl().style.width = layoutRect.minW + 'px';
33569 				layoutRect.startMinH = layoutRect.h = layoutRect.minH = Math.min(layoutRect.maxH, DomUtils.getSize(self.getEl()).height);
33570 			}
33571 
33572 			return layoutRect;
33573 		},
33574 
33575 		/**
33576 		 * Repaints the control after a layout operation.
33577 		 *
33578 		 * @method repaint
33579 		 */
33580 		repaint: function() {
33581 			var self = this;
33582 
33583 			if (!self.settings.multiline) {
33584 				self.getEl().style.lineHeight = self.layoutRect().h + 'px';
33585 			}
33586 
33587 			return self._super();
33588 		},
33589 
33590 		/**
33591 		 * Sets/gets the current label text.
33592 		 *
33593 		 * @method text
33594 		 * @param {String} [text] New label text.
33595 		 * @return {String|tinymce.ui.Label} Current text or current label instance.
33596 		 */
33597 		text: function(text) {
33598 			var self = this;
33599 
33600 			if (self._rendered && text) {
33601 				this.innerHtml(self.encode(text));
33602 			}
33603 
33604 			return self._super(text);
33605 		},
33606 
33607 		/**
33608 		 * Renders the control as a HTML string.
33609 		 *
33610 		 * @method renderHtml
33611 		 * @return {String} HTML representing the control.
33612 		 */
33613 		renderHtml: function() {
33614 			var self = this, forId = self.settings.forId;
33615 
33616 			return (
33617 				'<label id="' + self._id + '" class="' + self.classes() + '"' + (forId ? ' for="' + forId + '"' : '') + '>' +
33618 					self.encode(self._text) +
33619 				'</label>'
33620 			);
33621 		}
33622 	});
33623 });
33624 
33625 // Included from: js/tinymce/classes/ui/Toolbar.js
33626 
33627 /**
33628  * Toolbar.js
33629  *
33630  * Copyright, Moxiecode Systems AB
33631  * Released under LGPL License.
33632  *
33633  * License: http://www.tinymce.com/license
33634  * Contributing: http://www.tinymce.com/contributing
33635  */
33636 
33637 /**
33638  * Creates a new toolbar.
33639  *
33640  * @class tinymce.ui.Toolbar
33641  * @extends tinymce.ui.Container
33642  */
33643 define("tinymce/ui/Toolbar", [
33644 	"tinymce/ui/Container"
33645 ], function(Container) {
33646 	"use strict";
33647 
33648 	return Container.extend({
33649 		Defaults: {
33650 			role: 'toolbar',
33651 			layout: 'flow'
33652 		},
33653 
33654 		/**
33655 		 * Constructs a instance with the specified settings.
33656 		 *
33657 		 * @constructor
33658 		 * @param {Object} settings Name/value object with settings.
33659 		 */
33660 		init: function(settings) {
33661 			var self = this;
33662 
33663 			self._super(settings);
33664 			self.addClass('toolbar');
33665 		},
33666 
33667 		/**
33668 		 * Called after the control has been rendered.
33669 		 *
33670 		 * @method postRender
33671 		 */
33672 		postRender: function() {
33673 			var self = this;
33674 
33675 			self.items().addClass('toolbar-item');
33676 
33677 			return self._super();
33678 		}
33679 	});
33680 });
33681 
33682 // Included from: js/tinymce/classes/ui/MenuBar.js
33683 
33684 /**
33685  * MenuBar.js
33686  *
33687  * Copyright, Moxiecode Systems AB
33688  * Released under LGPL License.
33689  *
33690  * License: http://www.tinymce.com/license
33691  * Contributing: http://www.tinymce.com/contributing
33692  */
33693 
33694 /**
33695  * Creates a new menubar.
33696  *
33697  * @-x-less MenuBar.less
33698  * @class tinymce.ui.MenuBar
33699  * @extends tinymce.ui.Container
33700  */
33701 define("tinymce/ui/MenuBar", [
33702 	"tinymce/ui/Toolbar"
33703 ], function(Toolbar) {
33704 	"use strict";
33705 
33706 	return Toolbar.extend({
33707 		Defaults: {
33708 			role: 'menubar',
33709 			containerCls: 'menubar',
33710 			ariaRoot: true,
33711 			defaults: {
33712 				type: 'menubutton'
33713 			}
33714 		}
33715 	});
33716 });
33717 
33718 // Included from: js/tinymce/classes/ui/MenuButton.js
33719 
33720 /**
33721  * MenuButton.js
33722  *
33723  * Copyright, Moxiecode Systems AB
33724  * Released under LGPL License.
33725  *
33726  * License: http://www.tinymce.com/license
33727  * Contributing: http://www.tinymce.com/contributing
33728  */
33729 
33730 /**
33731  * Creates a new menu button.
33732  *
33733  * @-x-less MenuButton.less
33734  * @class tinymce.ui.MenuButton
33735  * @extends tinymce.ui.Button
33736  */
33737 define("tinymce/ui/MenuButton", [
33738 	"tinymce/ui/Button",
33739 	"tinymce/ui/Factory",
33740 	"tinymce/ui/MenuBar"
33741 ], function(Button, Factory, MenuBar) {
33742 	"use strict";
33743 
33744 	// TODO: Maybe add as some global function
33745 	function isChildOf(node, parent) {
33746 		while (node) {
33747 			if (parent === node) {
33748 				return true;
33749 			}
33750 
33751 			node = node.parentNode;
33752 		}
33753 
33754 		return false;
33755 	}
33756 
33757 	var MenuButton = Button.extend({
33758 		/**
33759 		 * Constructs a instance with the specified settings.
33760 		 *
33761 		 * @constructor
33762 		 * @param {Object} settings Name/value object with settings.
33763 		 */
33764 		init: function(settings) {
33765 			var self = this;
33766 
33767 			self._renderOpen = true;
33768 			self._super(settings);
33769 
33770 			self.addClass('menubtn');
33771 
33772 			if (settings.fixedWidth) {
33773 				self.addClass('fixed-width');
33774 			}
33775 
33776 			self.aria('haspopup', true);
33777 			self.hasPopup = true;
33778 		},
33779 
33780 		/**
33781 		 * Shows the menu for the button.
33782 		 *
33783 		 * @method showMenu
33784 		 */
33785 		showMenu: function() {
33786 			var self = this, settings = self.settings, menu;
33787 
33788 			if (self.menu && self.menu.visible()) {
33789 				return self.hideMenu();
33790 			}
33791 
33792 			if (!self.menu) {
33793 				menu = settings.menu || [];
33794 
33795 				// Is menu array then auto constuct menu control
33796 				if (menu.length) {
33797 					menu = {
33798 						type: 'menu',
33799 						items: menu
33800 					};
33801 				} else {
33802 					menu.type = menu.type || 'menu';
33803 				}
33804 
33805 				self.menu = Factory.create(menu).parent(self).renderTo();
33806 				self.fire('createmenu');
33807 				self.menu.reflow();
33808 				self.menu.on('cancel', function(e) {
33809 					if (e.control.parent() === self.menu) {
33810 						e.stopPropagation();
33811 						self.focus();
33812 						self.hideMenu();
33813 					}
33814 				});
33815 
33816 				// Move focus to button when a menu item is selected/clicked
33817 				self.menu.on('select', function() {
33818 					self.focus();
33819 				});
33820 
33821 				self.menu.on('show hide', function(e) {
33822 					if (e.control == self.menu) {
33823 						self.activeMenu(e.type == 'show');
33824 					}
33825 
33826 					self.aria('expanded', e.type == 'show');
33827 				}).fire('show');
33828 			}
33829 
33830 			self.menu.show();
33831 			self.menu.layoutRect({w: self.layoutRect().w});
33832 			self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
33833 		},
33834 
33835 		/**
33836 		 * Hides the menu for the button.
33837 		 *
33838 		 * @method hideMenu
33839 		 */
33840 		hideMenu: function() {
33841 			var self = this;
33842 
33843 			if (self.menu) {
33844 				self.menu.items().each(function(item) {
33845 					if (item.hideMenu) {
33846 						item.hideMenu();
33847 					}
33848 				});
33849 
33850 				self.menu.hide();
33851 			}
33852 		},
33853 
33854 		/**
33855 		 * Sets the active menu state.
33856 		 *
33857 		 * @private
33858 		 */
33859 		activeMenu: function(state) {
33860 			this.toggleClass('active', state);
33861 		},
33862 
33863 		/**
33864 		 * Renders the control as a HTML string.
33865 		 *
33866 		 * @method renderHtml
33867 		 * @return {String} HTML representing the control.
33868 		 */
33869 		renderHtml: function() {
33870 			var self = this, id = self._id, prefix = self.classPrefix;
33871 			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
33872 
33873 			self.aria('role', self.parent() instanceof MenuBar ? 'menuitem' : 'button');
33874 
33875 			return (
33876 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1" aria-labelledby="' + id + '">' +
33877 					'<button id="' + id + '-open" role="presentation" type="button" tabindex="-1">' +
33878 						(icon ? '<i class="' + icon + '"></i>' : '') +
33879 						'<span>' + (self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') + '</span>' +
33880 						' <i class="' + prefix + 'caret"></i>' +
33881 					'</button>' +
33882 				'</div>'
33883 			);
33884 		},
33885 
33886 		/**
33887 		 * Gets invoked after the control has been rendered.
33888 		 *
33889 		 * @method postRender
33890 		 */
33891 		postRender: function() {
33892 			var self = this;
33893 
33894 			self.on('click', function(e) {
33895 				if (e.control === self && isChildOf(e.target, self.getEl())) {
33896 					self.showMenu();
33897 
33898 					if (e.aria) {
33899 						self.menu.items()[0].focus();
33900 					}
33901 				}
33902 			});
33903 
33904 			self.on('mouseenter', function(e) {
33905 				var overCtrl = e.control, parent = self.parent(), hasVisibleSiblingMenu;
33906 
33907 				if (overCtrl && parent && overCtrl instanceof MenuButton && overCtrl.parent() == parent) {
33908 					parent.items().filter('MenuButton').each(function(ctrl) {
33909 						if (ctrl.hideMenu && ctrl != overCtrl) {
33910 							if (ctrl.menu && ctrl.menu.visible()) {
33911 								hasVisibleSiblingMenu = true;
33912 							}
33913 
33914 							ctrl.hideMenu();
33915 						}
33916 					});
33917 
33918 					if (hasVisibleSiblingMenu) {
33919 						overCtrl.focus(); // Fix for: #5887
33920 						overCtrl.showMenu();
33921 					}
33922 				}
33923 			});
33924 
33925 			return self._super();
33926 		},
33927 
33928 		/**
33929 		 * Sets/gets the current button text.
33930 		 *
33931 		 * @method text
33932 		 * @param {String} [text] New button text.
33933 		 * @return {String|tinymce.ui.MenuButton} Current text or current MenuButton instance.
33934 		 */
33935 		text: function(text) {
33936 			var self = this, i, children;
33937 
33938 			if (self._rendered) {
33939 				children = self.getEl('open').getElementsByTagName('span');
33940 				for (i = 0; i < children.length; i++) {
33941 					children[i].innerHTML = (self.settings.icon && text ? '\u00a0' : '') + self.encode(text);
33942 				}
33943 			}
33944 
33945 			return this._super(text);
33946 		},
33947 
33948 		/**
33949 		 * Removes the control and it's menus.
33950 		 *
33951 		 * @method remove
33952 		 */
33953 		remove: function() {
33954 			this._super();
33955 
33956 			if (this.menu) {
33957 				this.menu.remove();
33958 			}
33959 		}
33960 	});
33961 
33962 	return MenuButton;
33963 });
33964 
33965 // Included from: js/tinymce/classes/ui/ListBox.js
33966 
33967 /**
33968  * ListBox.js
33969  *
33970  * Copyright, Moxiecode Systems AB
33971  * Released under LGPL License.
33972  *
33973  * License: http://www.tinymce.com/license
33974  * Contributing: http://www.tinymce.com/contributing
33975  */
33976 
33977 /**
33978  * Creates a new list box control.
33979  *
33980  * @-x-less ListBox.less
33981  * @class tinymce.ui.ListBox
33982  * @extends tinymce.ui.MenuButton
33983  */
33984 define("tinymce/ui/ListBox", [
33985 	"tinymce/ui/MenuButton"
33986 ], function(MenuButton) {
33987 	"use strict";
33988 
33989 	return MenuButton.extend({
33990 		/**
33991 		 * Constructs a instance with the specified settings.
33992 		 *
33993 		 * @constructor
33994 		 * @param {Object} settings Name/value object with settings.
33995 		 * @setting {Array} values Array with values to add to list box.
33996 		 */
33997 		init: function(settings) {
33998 			var self = this, values, i, selected, selectedText, lastItemCtrl;
33999 
34000 			self._values = values = settings.values;
34001 			if (values) {
34002 				for (i = 0; i < values.length; i++) {
34003 					selected = values[i].selected || settings.value === values[i].value;
34004 
34005 					if (selected) {
34006 						selectedText = selectedText || values[i].text;
34007 						self._value = values[i].value;
34008 						break;
34009 					}
34010 				}
34011 
34012 				// Default with first item
34013 				if (!selected && values.length > 0) {
34014 					selectedText = values[0].text;
34015 					self._value = values[0].value;
34016 				}
34017 
34018 				settings.menu = values;
34019 			}
34020 
34021 			settings.text = settings.text || selectedText || values[0].text;
34022 
34023 			self._super(settings);
34024 			self.addClass('listbox');
34025 
34026 			self.on('select', function(e) {
34027 				var ctrl = e.control;
34028 
34029 				if (lastItemCtrl) {
34030 					e.lastControl = lastItemCtrl;
34031 				}
34032 
34033 				if (settings.multiple) {
34034 					ctrl.active(!ctrl.active());
34035 				} else {
34036 					self.value(e.control.settings.value);
34037 				}
34038 
34039 				lastItemCtrl = ctrl;
34040 			});
34041 		},
34042 
34043 		/**
34044 		 * Getter/setter function for the control value.
34045 		 *
34046 		 * @method value
34047 		 * @param {String} [value] Value to be set.
34048 		 * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation.
34049 		 */
34050 		value: function(value) {
34051 			var self = this, active, selectedText, menu, i;
34052 
34053 			function activateByValue(menu, value) {
34054 				menu.items().each(function(ctrl) {
34055 					active = ctrl.value() === value;
34056 
34057 					if (active) {
34058 						selectedText = selectedText || ctrl.text();
34059 					}
34060 
34061 					ctrl.active(active);
34062 
34063 					if (ctrl.menu) {
34064 						activateByValue(ctrl.menu, value);
34065 					}
34066 				});
34067 			}
34068 
34069 			if (typeof(value) != "undefined") {
34070 				if (self.menu) {
34071 					activateByValue(self.menu, value);
34072 				} else {
34073 					menu = self.settings.menu;
34074 					for (i = 0; i < menu.length; i++) {
34075 						active = menu[i].value == value;
34076 
34077 						if (active) {
34078 							selectedText = selectedText || menu[i].text;
34079 						}
34080 
34081 						menu[i].active = active;
34082 					}
34083 				}
34084 
34085 				self.text(selectedText || this.settings.text);
34086 			}
34087 
34088 			return self._super(value);
34089 		}
34090 	});
34091 });
34092 
34093 // Included from: js/tinymce/classes/ui/MenuItem.js
34094 
34095 /**
34096  * MenuItem.js
34097  *
34098  * Copyright, Moxiecode Systems AB
34099  * Released under LGPL License.
34100  *
34101  * License: http://www.tinymce.com/license
34102  * Contributing: http://www.tinymce.com/contributing
34103  */
34104 
34105 /**
34106  * Creates a new menu item.
34107  *
34108  * @-x-less MenuItem.less
34109  * @class tinymce.ui.MenuItem
34110  * @extends tinymce.ui.Control
34111  */
34112 define("tinymce/ui/MenuItem", [
34113 	"tinymce/ui/Widget",
34114 	"tinymce/ui/Factory",
34115 	"tinymce/Env"
34116 ], function(Widget, Factory, Env) {
34117 	"use strict";
34118 
34119 	return Widget.extend({
34120 		Defaults: {
34121 			border: 0,
34122 			role: 'menuitem'
34123 		},
34124 
34125 		/**
34126 		 * Constructs a instance with the specified settings.
34127 		 *
34128 		 * @constructor
34129 		 * @param {Object} settings Name/value object with settings.
34130 		 * @setting {Boolean} selectable Selectable menu.
34131 		 * @setting {Array} menu Submenu array with items.
34132 		 * @setting {String} shortcut Shortcut to display for menu item. Example: Ctrl+X
34133 		 */
34134 		init: function(settings) {
34135 			var self = this;
34136 
34137 			self.hasPopup = true;
34138 
34139 			self._super(settings);
34140 
34141 			settings = self.settings;
34142 
34143 			self.addClass('menu-item');
34144 
34145 			if (settings.menu) {
34146 				self.addClass('menu-item-expand');
34147 			}
34148 
34149 			if (settings.preview) {
34150 				self.addClass('menu-item-preview');
34151 			}
34152 
34153 			if (self._text === '-' || self._text === '|') {
34154 				self.addClass('menu-item-sep');
34155 				self.aria('role', 'separator');
34156 				self._text = '-';
34157 			}
34158 
34159 			if (settings.selectable) {
34160 				self.aria('role', 'menuitemcheckbox');
34161 				self.addClass('menu-item-checkbox');
34162 				settings.icon = 'selected';
34163 			}
34164 
34165 			if (!settings.preview && !settings.selectable) {
34166 				self.addClass('menu-item-normal');
34167 			}
34168 
34169 			self.on('mousedown', function(e) {
34170 				e.preventDefault();
34171 			});
34172 
34173 			if (settings.menu && !settings.ariaHideMenu) {
34174 				self.aria('haspopup', true);
34175 			}
34176 		},
34177 
34178 		/**
34179 		 * Returns true/false if the menuitem has sub menu.
34180 		 *
34181 		 * @method hasMenus
34182 		 * @return {Boolean} True/false state if it has submenu.
34183 		 */
34184 		hasMenus: function() {
34185 			return !!this.settings.menu;
34186 		},
34187 
34188 		/**
34189 		 * Shows the menu for the menu item.
34190 		 *
34191 		 * @method showMenu
34192 		 */
34193 		showMenu: function() {
34194 			var self = this, settings = self.settings, menu, parent = self.parent();
34195 
34196 			parent.items().each(function(ctrl) {
34197 				if (ctrl !== self) {
34198 					ctrl.hideMenu();
34199 				}
34200 			});
34201 
34202 			if (settings.menu) {
34203 				menu = self.menu;
34204 
34205 				if (!menu) {
34206 					menu = settings.menu;
34207 
34208 					// Is menu array then auto constuct menu control
34209 					if (menu.length) {
34210 						menu = {
34211 							type: 'menu',
34212 							items: menu
34213 						};
34214 					} else {
34215 						menu.type = menu.type || 'menu';
34216 					}
34217 
34218 					if (parent.settings.itemDefaults) {
34219 						menu.itemDefaults = parent.settings.itemDefaults;
34220 					}
34221 
34222 					menu = self.menu = Factory.create(menu).parent(self).renderTo();
34223 					menu.reflow();
34224 					menu.fire('show');
34225 					menu.on('cancel', function(e) {
34226 						e.stopPropagation();
34227 						self.focus();
34228 						menu.hide();
34229 					});
34230 
34231 					menu.on('hide', function(e) {
34232 						if (e.control === menu) {
34233 							self.removeClass('selected');
34234 						}
34235 					});
34236 
34237 					menu.submenu = true;
34238 				} else {
34239 					menu.show();
34240 				}
34241 
34242 				menu._parentMenu = parent;
34243 
34244 				menu.addClass('menu-sub');
34245 
34246 				var rel = menu.testMoveRel(
34247 					self.getEl(),
34248 					self.isRtl() ? ['tl-tr', 'bl-br', 'tr-tl', 'br-bl'] : ['tr-tl', 'br-bl', 'tl-tr', 'bl-br']
34249 				);
34250 
34251 				menu.moveRel(self.getEl(), rel);
34252 				menu.rel = rel;
34253 
34254 				rel = 'menu-sub-' + rel;
34255 				menu.removeClass(menu._lastRel);
34256 				menu.addClass(rel);
34257 				menu._lastRel = rel;
34258 
34259 				self.addClass('selected');
34260 				self.aria('expanded', true);
34261 			}
34262 		},
34263 
34264 		/**
34265 		 * Hides the menu for the menu item.
34266 		 *
34267 		 * @method hideMenu
34268 		 */
34269 		hideMenu: function() {
34270 			var self = this;
34271 
34272 			if (self.menu) {
34273 				self.menu.items().each(function(item) {
34274 					if (item.hideMenu) {
34275 						item.hideMenu();
34276 					}
34277 				});
34278 
34279 				self.menu.hide();
34280 				self.aria('expanded', false);
34281 			}
34282 
34283 			return self;
34284 		},
34285 
34286 		/**
34287 		 * Renders the control as a HTML string.
34288 		 *
34289 		 * @method renderHtml
34290 		 * @return {String} HTML representing the control.
34291 		 */
34292 		renderHtml: function() {
34293 			var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.encode(self._text);
34294 			var icon = self.settings.icon, image = '', shortcut = settings.shortcut;
34295 
34296 			if (icon) {
34297 				self.parent().addClass('menu-has-icons');
34298 			}
34299 
34300 			if (settings.image) {
34301 				icon = 'none';
34302 				image = ' style="background-image: url(\'' + settings.image + '\')"';
34303 			}
34304 
34305 			if (shortcut && Env.mac) {
34306 				// format shortcut for Mac
34307 				shortcut = shortcut.replace(/ctrl\+alt\+/i, '⌥⌘'); // ctrl+cmd
34308 				shortcut = shortcut.replace(/ctrl\+/i, '⌘'); // ctrl symbol
34309 				shortcut = shortcut.replace(/alt\+/i, '⌥'); // cmd symbol
34310 				shortcut = shortcut.replace(/shift\+/i, '⇧'); // shift symbol
34311 			}
34312 
34313 			icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none');
34314 
34315 			return (
34316 				'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1">' +
34317 					(text !== '-' ? '<i class="' + icon + '"' + image + '></i>\u00a0' : '') +
34318 					(text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') +
34319 					(shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' + shortcut + '</div>' : '') +
34320 					(settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') +
34321 				'</div>'
34322 			);
34323 		},
34324 
34325 		/**
34326 		 * Gets invoked after the control has been rendered.
34327 		 *
34328 		 * @method postRender
34329 		 */
34330 		postRender: function() {
34331 			var self = this, settings = self.settings;
34332 
34333 			var textStyle = settings.textStyle;
34334 			if (typeof(textStyle) == "function") {
34335 				textStyle = textStyle.call(this);
34336 			}
34337 
34338 			if (textStyle) {
34339 				var textElm = self.getEl('text');
34340 				if (textElm) {
34341 					textElm.setAttribute('style', textStyle);
34342 				}
34343 			}
34344 
34345 			self.on('mouseenter click', function(e) {
34346 				if (e.control === self) {
34347 					if (!settings.menu && e.type === 'click') {
34348 						self.fire('select');
34349 						self.parent().hideAll();
34350 					} else {
34351 						self.showMenu();
34352 
34353 						if (e.aria) {
34354 							self.menu.focus(true);
34355 						}
34356 					}
34357 				}
34358 			});
34359 
34360 			self._super();
34361 
34362 			return self;
34363 		},
34364 
34365 		active: function(state) {
34366 			if (typeof(state) != "undefined") {
34367 				this.aria('checked', state);
34368 			}
34369 
34370 			return this._super(state);
34371 		},
34372 
34373 		/**
34374 		 * Removes the control and it's menus.
34375 		 *
34376 		 * @method remove
34377 		 */
34378 		remove: function() {
34379 			this._super();
34380 
34381 			if (this.menu) {
34382 				this.menu.remove();
34383 			}
34384 		}
34385 	});
34386 });
34387 
34388 // Included from: js/tinymce/classes/ui/Menu.js
34389 
34390 /**
34391  * Menu.js
34392  *
34393  * Copyright, Moxiecode Systems AB
34394  * Released under LGPL License.
34395  *
34396  * License: http://www.tinymce.com/license
34397  * Contributing: http://www.tinymce.com/contributing
34398  */
34399 
34400 /**
34401  * Creates a new menu.
34402  *
34403  * @-x-less Menu.less
34404  * @class tinymce.ui.Menu
34405  * @extends tinymce.ui.FloatPanel
34406  */
34407 define("tinymce/ui/Menu", [
34408 	"tinymce/ui/FloatPanel",
34409 	"tinymce/ui/MenuItem",
34410 	"tinymce/util/Tools"
34411 ], function(FloatPanel, MenuItem, Tools) {
34412 	"use strict";
34413 
34414 	var Menu = FloatPanel.extend({
34415 		Defaults: {
34416 			defaultType: 'menuitem',
34417 			border: 1,
34418 			layout: 'stack',
34419 			role: 'application',
34420 			bodyRole: 'menu',
34421 			ariaRoot: true
34422 		},
34423 
34424 		/**
34425 		 * Constructs a instance with the specified settings.
34426 		 *
34427 		 * @constructor
34428 		 * @param {Object} settings Name/value object with settings.
34429 		 */
34430 		init: function(settings) {
34431 			var self = this;
34432 
34433 			settings.autohide = true;
34434 			settings.constrainToViewport = true;
34435 
34436 			if (settings.itemDefaults) {
34437 				var items = settings.items, i = items.length;
34438 
34439 				while (i--) {
34440 					items[i] = Tools.extend({}, settings.itemDefaults, items[i]);
34441 				}
34442 			}
34443 
34444 			self._super(settings);
34445 			self.addClass('menu');
34446 		},
34447 
34448 		/**
34449 		 * Repaints the control after a layout operation.
34450 		 *
34451 		 * @method repaint
34452 		 */
34453 		repaint: function() {
34454 			this.toggleClass('menu-align', true);
34455 
34456 			this._super();
34457 
34458 			this.getEl().style.height = '';
34459 			this.getEl('body').style.height = '';
34460 
34461 			return this;
34462 		},
34463 
34464 		/**
34465 		 * Hides/closes the menu.
34466 		 *
34467 		 * @method cancel
34468 		 */
34469 		cancel: function() {
34470 			var self = this;
34471 
34472 			self.hideAll();
34473 			self.fire('select');
34474 		},
34475 
34476 		/**
34477 		 * Hide menu and all sub menus.
34478 		 *
34479 		 * @method hideAll
34480 		 */
34481 		hideAll: function() {
34482 			var self = this;
34483 
34484 			this.find('menuitem').exec('hideMenu');
34485 
34486 			return self._super();
34487 		},
34488 /*
34489 		getContainerElm: function() {
34490 			var doc = document, id = this.classPrefix + 'menucontainer';
34491 
34492 			var elm = doc.getElementById(id);
34493 			if (!elm) {
34494 				elm = doc.createElement('div');
34495 				elm.id = id;
34496 				elm.setAttribute('role', 'application');
34497 				elm.className = this.classPrefix + '-reset';
34498 				elm.style.position = 'absolute';
34499 				elm.style.top = elm.style.left = '0';
34500 				elm.style.overflow = 'visible';
34501 				doc.body.appendChild(elm);
34502 			}
34503 
34504 			return elm;
34505 		},
34506 */
34507 		/**
34508 		 * Invoked before the menu is rendered.
34509 		 *
34510 		 * @method preRender
34511 		 */
34512 		preRender: function() {
34513 			var self = this;
34514 
34515 			self.items().each(function(ctrl) {
34516 				var settings = ctrl.settings;
34517 
34518 				if (settings.icon || settings.selectable) {
34519 					self._hasIcons = true;
34520 					return false;
34521 				}
34522 			});
34523 
34524 			return self._super();
34525 		}
34526 	});
34527 
34528 	return Menu;
34529 });
34530 
34531 // Included from: js/tinymce/classes/ui/Radio.js
34532 
34533 /**
34534  * Radio.js
34535  *
34536  * Copyright, Moxiecode Systems AB
34537  * Released under LGPL License.
34538  *
34539  * License: http://www.tinymce.com/license
34540  * Contributing: http://www.tinymce.com/contributing
34541  */
34542 
34543 /**
34544  * Creates a new radio button.
34545  *
34546  * @-x-less Radio.less
34547  * @class tinymce.ui.Radio
34548  * @extends tinymce.ui.Checkbox
34549  */
34550 define("tinymce/ui/Radio", [
34551 	"tinymce/ui/Checkbox"
34552 ], function(Checkbox) {
34553 	"use strict";
34554 
34555 	return Checkbox.extend({
34556 		Defaults: {
34557 			classes: "radio",
34558 			role: "radio"
34559 		}
34560 	});
34561 });
34562 
34563 // Included from: js/tinymce/classes/ui/ResizeHandle.js
34564 
34565 /**
34566  * ResizeHandle.js
34567  *
34568  * Copyright, Moxiecode Systems AB
34569  * Released under LGPL License.
34570  *
34571  * License: http://www.tinymce.com/license
34572  * Contributing: http://www.tinymce.com/contributing
34573  */
34574 
34575 /**
34576  * Renders a resize handle that fires ResizeStart, Resize and ResizeEnd events.
34577  *
34578  * @-x-less ResizeHandle.less
34579  * @class tinymce.ui.ResizeHandle
34580  * @extends tinymce.ui.Widget
34581  */
34582 define("tinymce/ui/ResizeHandle", [
34583 	"tinymce/ui/Widget",
34584 	"tinymce/ui/DragHelper"
34585 ], function(Widget, DragHelper) {
34586 	"use strict";
34587 
34588 	return Widget.extend({
34589 		/**
34590 		 * Renders the control as a HTML string.
34591 		 *
34592 		 * @method renderHtml
34593 		 * @return {String} HTML representing the control.
34594 		 */
34595 		renderHtml: function() {
34596 			var self = this, prefix = self.classPrefix;
34597 
34598 			self.addClass('resizehandle');
34599 
34600 			if (self.settings.direction == "both") {
34601 				self.addClass('resizehandle-both');
34602 			}
34603 
34604 			self.canFocus = false;
34605 
34606 			return (
34607 				'<div id="' + self._id + '" class="' + self.classes() + '">' +
34608 					'<i class="' + prefix + 'ico ' + prefix + 'i-resize"></i>' +
34609 				'</div>'
34610 			);
34611 		},
34612 
34613 		/**
34614 		 * Called after the control has been rendered.
34615 		 *
34616 		 * @method postRender
34617 		 */
34618 		postRender: function() {
34619 			var self = this;
34620 
34621 			self._super();
34622 
34623 			self.resizeDragHelper = new DragHelper(this._id, {
34624 				start: function() {
34625 					self.fire('ResizeStart');
34626 				},
34627 
34628 				drag: function(e) {
34629 					if (self.settings.direction != "both") {
34630 						e.deltaX = 0;
34631 					}
34632 
34633 					self.fire('Resize', e);
34634 				},
34635 
34636 				stop: function() {
34637 					self.fire('ResizeEnd');
34638 				}
34639 			});
34640 		},
34641 
34642 		remove: function() {
34643 			if (this.resizeDragHelper) {
34644 				this.resizeDragHelper.destroy();
34645 			}
34646 
34647 			return this._super();
34648 		}
34649 	});
34650 });
34651 
34652 // Included from: js/tinymce/classes/ui/Spacer.js
34653 
34654 /**
34655  * Spacer.js
34656  *
34657  * Copyright, Moxiecode Systems AB
34658  * Released under LGPL License.
34659  *
34660  * License: http://www.tinymce.com/license
34661  * Contributing: http://www.tinymce.com/contributing
34662  */
34663 
34664 /**
34665  * Creates a spacer. This control is used in flex layouts for example.
34666  *
34667  * @-x-less Spacer.less
34668  * @class tinymce.ui.Spacer
34669  * @extends tinymce.ui.Widget
34670  */
34671 define("tinymce/ui/Spacer", [
34672 	"tinymce/ui/Widget"
34673 ], function(Widget) {
34674 	"use strict";
34675 
34676 	return Widget.extend({
34677 		/**
34678 		 * Renders the control as a HTML string.
34679 		 *
34680 		 * @method renderHtml
34681 		 * @return {String} HTML representing the control.
34682 		 */
34683 		renderHtml: function() {
34684 			var self = this;
34685 
34686 			self.addClass('spacer');
34687 			self.canFocus = false;
34688 
34689 			return '<div id="' + self._id + '" class="' + self.classes() + '"></div>';
34690 		}
34691 	});
34692 });
34693 
34694 // Included from: js/tinymce/classes/ui/SplitButton.js
34695 
34696 /**
34697  * SplitButton.js
34698  *
34699  * Copyright, Moxiecode Systems AB
34700  * Released under LGPL License.
34701  *
34702  * License: http://www.tinymce.com/license
34703  * Contributing: http://www.tinymce.com/contributing
34704  */
34705 
34706 /**
34707  * Creates a split button.
34708  *
34709  * @-x-less SplitButton.less
34710  * @class tinymce.ui.SplitButton
34711  * @extends tinymce.ui.Button
34712  */
34713 define("tinymce/ui/SplitButton", [
34714 	"tinymce/ui/MenuButton",
34715 	"tinymce/ui/DomUtils"
34716 ], function(MenuButton, DomUtils) {
34717 	return MenuButton.extend({
34718 		Defaults: {
34719 			classes: "widget btn splitbtn",
34720 			role: "button"
34721 		},
34722 
34723 		/**
34724 		 * Repaints the control after a layout operation.
34725 		 *
34726 		 * @method repaint
34727 		 */
34728 		repaint: function() {
34729 			var self = this, elm = self.getEl(), rect = self.layoutRect(), mainButtonElm, menuButtonElm;
34730 
34731 			self._super();
34732 
34733 			mainButtonElm = elm.firstChild;
34734 			menuButtonElm = elm.lastChild;
34735 
34736 			DomUtils.css(mainButtonElm, {
34737 				width: rect.w - DomUtils.getSize(menuButtonElm).width,
34738 				height: rect.h - 2
34739 			});
34740 
34741 			DomUtils.css(menuButtonElm, {
34742 				height: rect.h - 2
34743 			});
34744 
34745 			return self;
34746 		},
34747 
34748 		/**
34749 		 * Sets the active menu state.
34750 		 *
34751 		 * @private
34752 		 */
34753 		activeMenu: function(state) {
34754 			var self = this;
34755 
34756 			DomUtils.toggleClass(self.getEl().lastChild, self.classPrefix + 'active', state);
34757 		},
34758 
34759 		/**
34760 		 * Renders the control as a HTML string.
34761 		 *
34762 		 * @method renderHtml
34763 		 * @return {String} HTML representing the control.
34764 		 */
34765 		renderHtml: function() {
34766 			var self = this, id = self._id, prefix = self.classPrefix;
34767 			var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
34768 
34769 			return (
34770 				'<div id="' + id + '" class="' + self.classes() + '" role="button" tabindex="-1">' +
34771 					'<button type="button" hidefocus="1" tabindex="-1">' +
34772 						(icon ? '<i class="' + icon + '"></i>' : '') +
34773 						(self._text ? (icon ? ' ' : '') + self._text : '') +
34774 					'</button>' +
34775 					'<button type="button" class="' + prefix + 'open" hidefocus="1" tabindex="-1">' +
34776 						//(icon ? '<i class="' + icon + '"></i>' : '') +
34777 						(self._menuBtnText ? (icon ? '\u00a0' : '') + self._menuBtnText : '') +
34778 						' <i class="' + prefix + 'caret"></i>' +
34779 					'</button>' +
34780 				'</div>'
34781 			);
34782 		},
34783 
34784 		/**
34785 		 * Called after the control has been rendered.
34786 		 *
34787 		 * @method postRender
34788 		 */
34789 		postRender: function() {
34790 			var self = this, onClickHandler = self.settings.onclick;
34791 
34792 			self.on('click', function(e) {
34793 				var node = e.target;
34794 
34795 				if (e.control == this) {
34796 					// Find clicks that is on the main button
34797 					while (node) {
34798 						if ((e.aria && e.aria.key != 'down') || (node.nodeName == 'BUTTON' && node.className.indexOf('open') == -1)) {
34799 							e.stopImmediatePropagation();
34800 							onClickHandler.call(this, e);
34801 							return;
34802 						}
34803 
34804 						node = node.parentNode;
34805 					}
34806 				}
34807 			});
34808 
34809 			delete self.settings.onclick;
34810 
34811 			return self._super();
34812 		}
34813 	});
34814 });
34815 
34816 // Included from: js/tinymce/classes/ui/StackLayout.js
34817 
34818 /**
34819  * StackLayout.js
34820  *
34821  * Copyright, Moxiecode Systems AB
34822  * Released under LGPL License.
34823  *
34824  * License: http://www.tinymce.com/license
34825  * Contributing: http://www.tinymce.com/contributing
34826  */
34827 
34828 /**
34829  * This layout uses the browsers layout when the items are blocks.
34830  *
34831  * @-x-less StackLayout.less
34832  * @class tinymce.ui.StackLayout
34833  * @extends tinymce.ui.FlowLayout
34834  */
34835 define("tinymce/ui/StackLayout", [
34836 	"tinymce/ui/FlowLayout"
34837 ], function(FlowLayout) {
34838 	"use strict";
34839 
34840 	return FlowLayout.extend({
34841 		Defaults: {
34842 			containerClass: 'stack-layout',
34843 			controlClass: 'stack-layout-item',
34844 			endClass : 'break'
34845 		}
34846 	});
34847 });
34848 
34849 // Included from: js/tinymce/classes/ui/TabPanel.js
34850 
34851 /**
34852  * TabPanel.js
34853  *
34854  * Copyright, Moxiecode Systems AB
34855  * Released under LGPL License.
34856  *
34857  * License: http://www.tinymce.com/license
34858  * Contributing: http://www.tinymce.com/contributing
34859  */
34860 
34861 /**
34862  * Creates a tab panel control.
34863  *
34864  * @-x-less TabPanel.less
34865  * @class tinymce.ui.TabPanel
34866  * @extends tinymce.ui.Panel
34867  *
34868  * @setting {Number} activeTab Active tab index.
34869  */
34870 define("tinymce/ui/TabPanel", [
34871 	"tinymce/ui/Panel",
34872 	"tinymce/ui/DomUtils"
34873 ], function(Panel, DomUtils) {
34874 	"use strict";
34875 
34876 	return Panel.extend({
34877 		lastIdx: 0,
34878 
34879 		Defaults: {
34880 			layout: 'absolute',
34881 			defaults: {
34882 				type: 'panel'
34883 			}
34884 		},
34885 
34886 		/**
34887 		 * Activates the specified tab by index.
34888 		 *
34889 		 * @method activateTab
34890 		 * @param {Number} idx Index of the tab to activate.
34891 		 */
34892 		activateTab: function(idx) {
34893 			var activeTabElm;
34894 
34895 			if (this.activeTabId) {
34896 				activeTabElm = this.getEl(this.activeTabId);
34897 				DomUtils.removeClass(activeTabElm, this.classPrefix + 'active');
34898 				activeTabElm.setAttribute('aria-selected', "false");
34899 			}
34900 
34901 			this.activeTabId = 't' + idx;
34902 
34903 			activeTabElm = this.getEl('t' + idx);
34904 			activeTabElm.setAttribute('aria-selected', "true");
34905 			DomUtils.addClass(activeTabElm, this.classPrefix + 'active');
34906 
34907 			if (idx != this.lastIdx) {
34908 				this.items()[this.lastIdx].hide();
34909 				this.lastIdx = idx;
34910 			}
34911 
34912 			this.items()[idx].show().fire('showtab');
34913 			this.reflow();
34914 		},
34915 
34916 		/**
34917 		 * Renders the control as a HTML string.
34918 		 *
34919 		 * @method renderHtml
34920 		 * @return {String} HTML representing the control.
34921 		 */
34922 		renderHtml: function() {
34923 			var self = this, layout = self._layout, tabsHtml = '', prefix = self.classPrefix;
34924 
34925 			self.preRender();
34926 			layout.preRender(self);
34927 
34928 			self.items().each(function(ctrl, i) {
34929 				var id = self._id + '-t' + i;
34930 
34931 				ctrl.aria('role', 'tabpanel');
34932 				ctrl.aria('labelledby', id);
34933 
34934 				tabsHtml += (
34935 					'<div id="' + id + '" class="' + prefix + 'tab" ' +
34936 						'unselectable="on" role="tab" aria-controls="' + ctrl._id + '" aria-selected="false" tabIndex="-1">' +
34937 						self.encode(ctrl.settings.title) +
34938 					'</div>'
34939 				);
34940 			});
34941 
34942 			return (
34943 				'<div id="' + self._id + '" class="' + self.classes() + '" hidefocus="1" tabindex="-1">' +
34944 					'<div id="' + self._id + '-head" class="' + prefix + 'tabs" role="tablist">' +
34945 						tabsHtml +
34946 					'</div>' +
34947 					'<div id="' + self._id + '-body" class="' + self.classes('body') + '">' +
34948 						layout.renderHtml(self) +
34949 					'</div>' +
34950 				'</div>'
34951 			);
34952 		},
34953 
34954 		/**
34955 		 * Called after the control has been rendered.
34956 		 *
34957 		 * @method postRender
34958 		 */
34959 		postRender: function() {
34960 			var self = this;
34961 
34962 			self._super();
34963 
34964 			self.settings.activeTab = self.settings.activeTab || 0;
34965 			self.activateTab(self.settings.activeTab);
34966 
34967 			this.on('click', function(e) {
34968 				var targetParent = e.target.parentNode;
34969 
34970 				if (e.target.parentNode.id == self._id + '-head') {
34971 					var i = targetParent.childNodes.length;
34972 
34973 					while (i--) {
34974 						if (targetParent.childNodes[i] == e.target) {
34975 							self.activateTab(i);
34976 						}
34977 					}
34978 				}
34979 			});
34980 		},
34981 
34982 		/**
34983 		 * Initializes the current controls layout rect.
34984 		 * This will be executed by the layout managers to determine the
34985 		 * default minWidth/minHeight etc.
34986 		 *
34987 		 * @method initLayoutRect
34988 		 * @return {Object} Layout rect instance.
34989 		 */
34990 		initLayoutRect: function() {
34991 			var self = this, rect, minW, minH;
34992 
34993 			minW = DomUtils.getSize(self.getEl('head')).width;
34994 			minW = minW < 0 ? 0 : minW;
34995 			minH = 0;
34996 			self.items().each(function(item, i) {
34997 				minW = Math.max(minW, item.layoutRect().minW);
34998 				minH = Math.max(minH, item.layoutRect().minH);
34999 				if (self.settings.activeTab != i) {
35000 					item.hide();
35001 				}
35002 			});
35003 
35004 			self.items().each(function(ctrl) {
35005 				ctrl.settings.x = 0;
35006 				ctrl.settings.y = 0;
35007 				ctrl.settings.w = minW;
35008 				ctrl.settings.h = minH;
35009 
35010 				ctrl.layoutRect({
35011 					x: 0,
35012 					y: 0,
35013 					w: minW,
35014 					h: minH
35015 				});
35016 			});
35017 
35018 			var headH = DomUtils.getSize(self.getEl('head')).height;
35019 
35020 			self.settings.minWidth = minW;
35021 			self.settings.minHeight = minH + headH;
35022 
35023 			rect = self._super();
35024 			rect.deltaH += headH;
35025 			rect.innerH = rect.h - rect.deltaH;
35026 
35027 			return rect;
35028 		}
35029 	});
35030 });
35031 
35032 // Included from: js/tinymce/classes/ui/TextBox.js
35033 
35034 /**
35035  * TextBox.js
35036  *
35037  * Copyright, Moxiecode Systems AB
35038  * Released under LGPL License.
35039  *
35040  * License: http://www.tinymce.com/license
35041  * Contributing: http://www.tinymce.com/contributing
35042  */
35043 
35044 /**
35045  * Creates a new textbox.
35046  *
35047  * @-x-less TextBox.less
35048  * @class tinymce.ui.TextBox
35049  * @extends tinymce.ui.Widget
35050  */
35051 define("tinymce/ui/TextBox", [
35052 	"tinymce/ui/Widget",
35053 	"tinymce/ui/DomUtils"
35054 ], function(Widget, DomUtils) {
35055 	"use strict";
35056 
35057 	return Widget.extend({
35058 		/**
35059 		 * Constructs a instance with the specified settings.
35060 		 *
35061 		 * @constructor
35062 		 * @param {Object} settings Name/value object with settings.
35063 		 * @setting {Boolean} multiline True if the textbox is a multiline control.
35064 		 * @setting {Number} maxLength Max length for the textbox.
35065 		 * @setting {Number} size Size of the textbox in characters.
35066 		 */
35067 		init: function(settings) {
35068 			var self = this;
35069 
35070 			self._super(settings);
35071 
35072 			self._value = settings.value || '';
35073 			self.addClass('textbox');
35074 
35075 			if (settings.multiline) {
35076 				self.addClass('multiline');
35077 			} else {
35078 				// TODO: Rework this
35079 				self.on('keydown', function(e) {
35080 					if (e.keyCode == 13) {
35081 						self.parents().reverse().each(function(ctrl) {
35082 							e.preventDefault();
35083 
35084 							if (ctrl.hasEventListeners('submit') && ctrl.toJSON) {
35085 								ctrl.fire('submit', {data: ctrl.toJSON()});
35086 								return false;
35087 							}
35088 						});
35089 					}
35090 				});
35091 			}
35092 		},
35093 
35094 		/**
35095 		 * Getter/setter function for the disabled state.
35096 		 *
35097 		 * @method value
35098 		 * @param {Boolean} [state] State to be set.
35099 		 * @return {Boolean|tinymce.ui.ComboBox} True/false or self if it's a set operation.
35100 		 */
35101 		disabled: function(state) {
35102 			var self = this;
35103 
35104 			if (self._rendered && typeof(state) != 'undefined') {
35105 				self.getEl().disabled = state;
35106 			}
35107 
35108 			return self._super(state);
35109 		},
35110 
35111 		/**
35112 		 * Getter/setter function for the control value.
35113 		 *
35114 		 * @method value
35115 		 * @param {String} [value] Value to be set.
35116 		 * @return {String|tinymce.ui.ComboBox} Value or self if it's a set operation.
35117 		 */
35118 		value: function(value) {
35119 			var self = this;
35120 
35121 			if (typeof(value) != "undefined") {
35122 				self._value = value;
35123 
35124 				if (self._rendered) {
35125 					self.getEl().value = value;
35126 				}
35127 
35128 				return self;
35129 			}
35130 
35131 			if (self._rendered) {
35132 				return self.getEl().value;
35133 			}
35134 
35135 			return self._value;
35136 		},
35137 
35138 		/**
35139 		 * Repaints the control after a layout operation.
35140 		 *
35141 		 * @method repaint
35142 		 */
35143 		repaint: function() {
35144 			var self = this, style, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect;
35145 
35146 			style = self.getEl().style;
35147 			rect = self._layoutRect;
35148 			lastRepaintRect = self._lastRepaintRect || {};
35149 
35150 			// Detect old IE 7+8 add lineHeight to align caret vertically in the middle
35151 			var doc = document;
35152 			if (!self.settings.multiline && doc.all && (!doc.documentMode || doc.documentMode <= 8)) {
35153 				style.lineHeight = (rect.h - borderH) + 'px';
35154 			}
35155 
35156 			borderBox = self._borderBox;
35157 			borderW = borderBox.left + borderBox.right + 8;
35158 			borderH = borderBox.top + borderBox.bottom + (self.settings.multiline ? 8 : 0);
35159 
35160 			if (rect.x !== lastRepaintRect.x) {
35161 				style.left = rect.x + 'px';
35162 				lastRepaintRect.x = rect.x;
35163 			}
35164 
35165 			if (rect.y !== lastRepaintRect.y) {
35166 				style.top = rect.y + 'px';
35167 				lastRepaintRect.y = rect.y;
35168 			}
35169 
35170 			if (rect.w !== lastRepaintRect.w) {
35171 				style.width = (rect.w - borderW) + 'px';
35172 				lastRepaintRect.w = rect.w;
35173 			}
35174 
35175 			if (rect.h !== lastRepaintRect.h) {
35176 				style.height = (rect.h - borderH) + 'px';
35177 				lastRepaintRect.h = rect.h;
35178 			}
35179 
35180 			self._lastRepaintRect = lastRepaintRect;
35181 			self.fire('repaint', {}, false);
35182 
35183 			return self;
35184 		},
35185 
35186 		/**
35187 		 * Renders the control as a HTML string.
35188 		 *
35189 		 * @method renderHtml
35190 		 * @return {String} HTML representing the control.
35191 		 */
35192 		renderHtml: function() {
35193 			var self = this, id = self._id, settings = self.settings, value = self.encode(self._value, false), extraAttrs = '';
35194 
35195 			if ("spellcheck" in settings) {
35196 				extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
35197 			}
35198 
35199 			if (settings.maxLength) {
35200 				extraAttrs += ' maxlength="' + settings.maxLength + '"';
35201 			}
35202 
35203 			if (settings.size) {
35204 				extraAttrs += ' size="' + settings.size + '"';
35205 			}
35206 
35207 			if (settings.subtype) {
35208 				extraAttrs += ' type="' + settings.subtype + '"';
35209 			}
35210 
35211 			if (self.disabled()) {
35212 				extraAttrs += ' disabled="disabled"';
35213 			}
35214 
35215 			if (settings.multiline) {
35216 				return (
35217 					'<textarea id="' + id + '" class="' + self.classes() + '" ' +
35218 					(settings.rows ? ' rows="' + settings.rows + '"' : '') +
35219 					' hidefocus="1"' + extraAttrs + '>' + value +
35220 					'</textarea>'
35221 				);
35222 			}
35223 
35224 			return '<input id="' + id + '" class="' + self.classes() + '" value="' + value + '" hidefocus="1"' + extraAttrs + ' />';
35225 		},
35226 
35227 		/**
35228 		 * Called after the control has been rendered.
35229 		 *
35230 		 * @method postRender
35231 		 */
35232 		postRender: function() {
35233 			var self = this;
35234 
35235 			DomUtils.on(self.getEl(), 'change', function(e) {
35236 				self.fire('change', e);
35237 			});
35238 
35239 			return self._super();
35240 		},
35241 
35242 		remove: function() {
35243 			DomUtils.off(this.getEl());
35244 			this._super();
35245 		}
35246 	});
35247 });
35248 
35249 // Included from: js/tinymce/classes/ui/Throbber.js
35250 
35251 /**
35252  * Throbber.js
35253  *
35254  * Copyright, Moxiecode Systems AB
35255  * Released under LGPL License.
35256  *
35257  * License: http://www.tinymce.com/license
35258  * Contributing: http://www.tinymce.com/contributing
35259  */
35260 
35261 /**
35262  * This class enables you to display a Throbber for any element.
35263  *
35264  * @-x-less Throbber.less
35265  * @class tinymce.ui.Throbber
35266  */
35267 define("tinymce/ui/Throbber", [
35268 	"tinymce/ui/DomUtils",
35269 	"tinymce/ui/Control"
35270 ], function(DomUtils, Control) {
35271 	"use strict";
35272 
35273 	/**
35274 	 * Constructs a new throbber.
35275 	 *
35276 	 * @constructor
35277 	 * @param {Element} elm DOM Html element to display throbber in.
35278 	 * @param {Boolean} inline Optional true/false state if the throbber should be appended to end of element for infinite scroll.
35279 	 */
35280 	return function(elm, inline) {
35281 		var self = this, state, classPrefix = Control.classPrefix;
35282 
35283 		/**
35284 		 * Shows the throbber.
35285 		 *
35286 		 * @method show
35287 		 * @param {Number} [time] Time to wait before showing.
35288 		 * @return {tinymce.ui.Throbber} Current throbber instance.
35289 		 */
35290 		self.show = function(time) {
35291 			self.hide();
35292 
35293 			state = true;
35294 
35295 			window.setTimeout(function() {
35296 				if (state) {
35297 					elm.appendChild(DomUtils.createFragment(
35298 						'<div class="' + classPrefix + 'throbber' + (inline ? ' ' + classPrefix + 'throbber-inline' : '') + '"></div>'
35299 					));
35300 				}
35301 			}, time || 0);
35302 
35303 			return self;
35304 		};
35305 
35306 		/**
35307 		 * Hides the throbber.
35308 		 *
35309 		 * @method hide
35310 		 * @return {tinymce.ui.Throbber} Current throbber instance.
35311 		 */
35312 		self.hide = function() {
35313 			var child = elm.lastChild;
35314 
35315 			if (child && child.className.indexOf('throbber') != -1) {
35316 				child.parentNode.removeChild(child);
35317 			}
35318 
35319 			state = false;
35320 
35321 			return self;
35322 		};
35323 	};
35324 });
35325 
35326 expose(["tinymce/dom/EventUtils","tinymce/dom/Sizzle","tinymce/dom/DomQuery","tinymce/html/Styles","tinymce/dom/TreeWalker","tinymce/util/Tools","tinymce/dom/Range","tinymce/html/Entities","tinymce/Env","tinymce/dom/DOMUtils","tinymce/dom/ScriptLoader","tinymce/AddOnManager","tinymce/html/Node","tinymce/html/Schema","tinymce/html/SaxParser","tinymce/html/DomParser","tinymce/html/Writer","tinymce/html/Serializer","tinymce/dom/Serializer","tinymce/dom/TridentSelection","tinymce/util/VK","tinymce/dom/ControlSelection","tinymce/dom/Selection","tinymce/Formatter","tinymce/UndoManager","tinymce/EnterKey","tinymce/ForceBlocks","tinymce/EditorCommands","tinymce/util/URI","tinymce/util/Class","tinymce/util/EventDispatcher","tinymce/ui/Selector","tinymce/ui/Collection","tinymce/ui/DomUtils","tinymce/ui/Control","tinymce/ui/Factory","tinymce/ui/KeyboardNavigation","tinymce/ui/Container","tinymce/ui/DragHelper","tinymce/ui/Scrollable","tinymce/ui/Panel","tinymce/ui/Movable","tinymce/ui/Resizable","tinymce/ui/FloatPanel","tinymce/ui/Window","tinymce/ui/MessageBox","tinymce/WindowManager","tinymce/util/Quirks","tinymce/util/Observable","tinymce/EditorObservable","tinymce/Shortcuts","tinymce/Editor","tinymce/util/I18n","tinymce/FocusManager","tinymce/EditorManager","tinymce/LegacyInput","tinymce/util/XHR","tinymce/util/JSON","tinymce/util/JSONRequest","tinymce/util/JSONP","tinymce/util/LocalStorage","tinymce/Compat","tinymce/ui/Layout","tinymce/ui/AbsoluteLayout","tinymce/ui/Tooltip","tinymce/ui/Widget","tinymce/ui/Button","tinymce/ui/ButtonGroup","tinymce/ui/Checkbox","tinymce/ui/PanelButton","tinymce/ui/ColorButton","tinymce/ui/ComboBox","tinymce/ui/Path","tinymce/ui/ElementPath","tinymce/ui/FormItem","tinymce/ui/Form","tinymce/ui/FieldSet","tinymce/ui/FilePicker","tinymce/ui/FitLayout","tinymce/ui/FlexLayout","tinymce/ui/FlowLayout","tinymce/ui/FormatControls","tinymce/ui/GridLayout","tinymce/ui/Iframe","tinymce/ui/Label","tinymce/ui/Toolbar","tinymce/ui/MenuBar","tinymce/ui/MenuButton","tinymce/ui/ListBox","tinymce/ui/MenuItem","tinymce/ui/Menu","tinymce/ui/Radio","tinymce/ui/ResizeHandle","tinymce/ui/Spacer","tinymce/ui/SplitButton","tinymce/ui/StackLayout","tinymce/ui/TabPanel","tinymce/ui/TextBox","tinymce/ui/Throbber"]);
35327 })(this);